Приветствую. После того как я полностью автоматизировал AD в организации, появилась острая необходимость в безопасной передаче пароля конечному пользователю. По регламенту организации сотрудники тех поддержки не должны видеть пароль но при этом должны как то его сбросить и передать. Решение было быстрым и тривиальным. Написал простенький инструмент. конвертировал его в EXE и отдал сотрудникам. Сразу же сообщу. В коде много обращений к API внутренних сервисов организации, у вас же это может сильно отличатся. например функции отправки СМС и получения инфы о пользователе завязаны на веб-серверах, у вас это может иметь другую реализацию. На худой конец, вы всегда можете закомментировать не нужные строки и пользоваться кор-функционалом с отправкой данных только на e-mail.

Тыкни
<#
Итак. Все обращения к базам данных HR у нас осуществляются по API. У вас же это может выполнятся разными способами.
#>
$global:headers = @{
"content-type" = "application/json"
"Authorization" = "Basic SOMEA123123123123123TOKEN"
}
# Запрос к API базы HR
function Invoke-APIRequest
{
param (
[Parameter(Mandatory)]
[string]$Uri,
[Parameter()]
[string]$Method = 'POST',
[Parameter()]
[string]$Body
)
return Invoke-RestMethod -Uri $Uri -Method $Method -Headers $global:headers -Body $Body
}
# Получение списка сотрудников
function Get-Employees
{
$body = @"
{
"get_child_departments":"True"
}
"@
return $(Invoke-APIRequest -Uri 'https://hr.domain.site/api/employees' -Body $body)
}
# Получение всех Департаментов банка
function Get-Departments
{
$body = @"
{
"get_child_departments":"True"
}
"@
return Invoke-APIRequest -Uri 'https://hr.domain.site/api/gate/department' -Body $body
}
# Очевидно
function get-userinf
{
$ipregex = "^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"
[string]$date = get-date -Format "yyyy.MM.dd hh-mm"
$ipaddress = Get-NetIPAddress | ? IPAddress -Match $ipregex | select IPAddress
$return = @{
"Username" = $($env:USERNAME)
"ComputerName" = $($env:COMPUTERNAME)
"IPAdress" = $ipaddress.IPAddress
"ExecutionDate" = $($date)
}
return ConvertTo-Json $return
}
# Очевидно
function Check-User
{
param (
[Parameter(Mandatory)]
[string]$samaccountname = "ttestesteronyan"
)
try {
# получаем пользователя
$user = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,Mobile,wWWHomePage,EmployeeID,EmployeeNumber,whenCreated |`
? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
$HRuser = (get-Employees).data `
| ? {if(($_.employee_id -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.system_id -eq $samaccountname) -or ($_.name -eq $user.name)){$_}} `
| select firstname, lastname, Name, department_id, Employee_ID, email, phone_number ,System_ID
# собираем данные
$output = @()
if($user){
$output += "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
$output += "Информация о пользователе $Username :"
$output += "====================================="
$output += "Логин: $($user.samaccountname)"
$output += "Статус учетки пользователя: $(if($user.enabled){"включена"}else{"выключена"})"
$output += "Имя: $($user.GivenName)"
$output += "Фамилия: $($user.Surname)"
$output += "ФИО: $($user.Name)"
$output += "Департамент: $($user.Department)"
$output += "Должность: $($user.Title)"
$output += "Мобильный телефон: $($user.Mobile)"
$output += "Личная почта: $($user.wWWHomePage)"
$output += "Номер сотрудника: $($user.EmployeeID)"
$output += "ID сотрудника: $($user.EmployeeNumber)"
$output += "Дата создания: $($user.whenCreated)"
$output += "====================================="
}
else{
$output = @()
}
if($HRuser){
$output += "Информация о пользователе $samaccountname в базе HR:"
$output += "====================================="
$output += "Имя: $($HRuser.firstname)"
$output += "Фамилия: $($HRuser.lastname)"
$output += "ФИО: $($HRuser.Name)"
$output += "Департамент: $(((Get-Departments).data | ? id -eq $HRuser.department_id).name)"
$output += "Должность: $($HRuser.post)"
$output += "Мобильный телефон: $($HRuser.phone_number)"
$output += "Личная почта: $($HRuser.email)"
$output += "Номер сотрудника: $($HRuser.Employee_ID)"
$output += "ID сотрудника: $($HRuser.System_ID)"
$output += "====================================="
}
if(!$user -and !$HRuser){
return "ПОльзователь не найден ни в АД ни в базе кадров."
}
elseif($user -and !$HRuser){
$output += "Пользователь не найден в базе кадров но есть в AD."
return ($output -join "`r`n")
}
elseif(!$user -and $HRuser){
$output += "Пользователь не найден в AD но найден в базе кадров."
return ($output -join "`r`n")
}
else{
return ($output -join "`r`n")
}
}
catch {
return "Ошибка при получении информации о пользователе $Username : $_"
}
}
# Очевидно
function Write-Log
{
param (
[Parameter(ValueFromPipeline = $true)]
[string]$Message,
[Parameter(Mandatory = $true)]
[string]$LogFilePath
)
process {
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$logEntry = "$($timestamp): $Message"
Add-Content -Path $LogFilePath -Value $logEntry
}
}
# Генерация пароля
function Get-RandomPassword
{
Param (
[Parameter(Mandatory)]
[int] $length
)
$CharSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.ToCharArray()
$SpecialCharSet = '!@'.ToCharArray()
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
$bytes = New-Object byte[]($length)
$rng.GetBytes($bytes)
$result = New-Object char[]($length)
# Генерация пароля без спецсимволов
for ($i = 0 ; $i -lt $length ; $i++) {
$result[$i] = $CharSet[$bytes[$i] % $CharSet.Length]
}
# Вставка специального символа в случайную позицию
$randomIndex = Get-Random -Minimum 0 -Maximum $length
$specialChar = $SpecialCharSet[$(Get-Random -Minimum 0 -Maximum $SpecialCharSet.Length)]
$result[$randomIndex] = $specialChar
return -join $result
}
# отправка смс по номеру телефона и мыло туда же
function ConvertTo-Lat
{
param ([string]$russian)
$alphabet = @{
"а" = "a"; "б" = "b"; "в" = "v"; "г" = "g"; "д" = "d";
"е" = "e"; "ё" = "yo"; "ж" = "j"; "з" = "z"; "и" = "i";
"й" = "i"; "к" = "k"; "л" = "l"; "м" = "m"; "н" = "n";
"о" = "o"; "п" = "p"; "р" = "r"; "с" = "s"; "т" = "t";
"у" = "u"; "ф" = "f"; "х" = "h"; "ц" = "c"; "ч" = "ch";
"ш" = "sh"; "щ" = "sch"; "ь" = ""; "ъ" = ""; "ы" = "y";
"э" = "e"; "ю" = "yu"; "я" = "ya"; " " = " ";
}
$russian_chars = $russian.ToCharArray()
$russian_in_lat = ""
foreach ($char in $russian_chars) {
$stringChar = $char.ToString()
$lowerChar = $stringChar.ToLower()
if ($alphabet.ContainsKey($lowerChar)) {
$translatedChar = $alphabet[$lowerChar]
if ($stringChar -ne $lowerChar) {
$translatedChar = $translatedChar[0].ToUpper() + $translatedChar.Substring(1)
}
$russian_in_lat += $translatedChar
} else {
$russian_in_lat += $char
}
}
return $russian_in_lat
}
# получение токена авторизации для смс-шлюза
function Get-Token
{
try{
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
$body = @"
{
`"username`": `"someUser`",
`"password`": `"PrettyStronGPa$$w0rd`"
}
"@
$response = Invoke-RestMethod 'https://smsgtw.domain.site/api/auth/token' -Method 'POST' -Headers $headers -Body $body
return $response
}
catch{
return $Error[0].Exception.Message
}
}
# отправка смс по номеру телефона
function Send-SMS
{
[CmdletBinding(SupportsShouldProcess = $true)]
param(
$AuthToken,
$phone_number,
$purpose,
$message
)
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
$headers.Add("Authorization", "Bearer $AuthToken")
$body = @"
{
`"purpose`": `"$purpose`",
`"phone_number`": `"$phone_number`",
`"message`": `"$message`",
}
"@
$response = Invoke-RestMethod 'https://smsgtw.domain.site/api/messages' -Method 'POST' -Headers $headers -Body $body
$response.data.when_sent | ConvertTo-Json
}
# отправка смс по номеру телефона и мыло туда же
function Send-Notification
{
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string] $Email,
[string] $PhoneNum,
[string] $EmailBody,
[string] $SmsBody
)
$p = ConvertTo-SecureString -String "PrettyStronGPa$$w0rd" -AsPlainText -Force
$u = "robot@domain.site"
$smtpCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $p
$hasEmail = -not [string]::IsNullOrWhiteSpace($Email)
$hasPhone = -not [string]::IsNullOrWhiteSpace($PhoneNum)
if (-not $hasEmail -and -not $hasPhone) {
return "Ошибка: не указан ни адрес электронной почты, ни номер телефона."
}
$combinedBody = $EmailBody, $SmsBody -join " | "
$sentEmail = $false
$sentSms = $false
try {
if ($hasEmail -and $hasPhone) {
Send-MailMessage -To $Email `
-From $u `
-Subject "Уведомление" `
-Body $EmailBody `
-SmtpServer "smtp.domain.site" `
-Credential $smtpCredential `
-UseSsl `
-Encoding UTF8
$sentEmail = $true
Send-SMS -AuthToken (Get-Token).access_token `
-phone_number $PhoneNum `
-purpose "Send User's data" `
-message "$SmsBody"
$sentSms = $true
}
elseif ($hasEmail) {
Send-MailMessage -To $Email `
-From $u `
-Subject "Уведомление" `
-Body $combinedBody `
-SmtpServer "smtp.domain.site" `
-Credential $smtpCredential `
-UseSsl `
-Encoding UTF8
$sentEmail = $true
}
else {
Send-SMS -AuthToken (Get-Token).access_token `
-phone_number $PhoneNum `
-purpose "Send User's data" `
-message "$((ConvertTo-Lat $combinedBody).replace("_"," "))"
$sentSms = $true
}
return [pscustomobject]@{
DateTime = Get-Date
EmailSent = $sentEmail
SmsSent = $sentSms
}
}
catch {
return $_.Exception
}
}
# Смена пароля пльзователя
function Reset-ADUserPassword
{
param($samaccountname)
try{
$chekADuser = Get-ADUser -Filter * -Properties GivenName,Surname,Name,Department,Title,MobilePhone,HomePage,EmployeeID,EmployeeNumber,whenCreated |`
? {if(($_.EmployeeID -eq $samaccountname) -or ($_.Name -eq $samaccountname) -or ($_.employeeNumber -eq $samaccountname) -or ($_.SamaccountName -eq $samaccountname)){$_}}
if(!$chekADuser){
$result = "$(get-date): Не найден пользователь"
}
else{
if(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
$passsword = Get-RandomPassword -length 10
try{
Set-ADAccountPassword -Identity $chekADuser.DistinguishedName `
-NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
-Confirm:$false
Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
Send-Notification -Email $chekADuser.HomePage `
-PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
-EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен и отправлен на мобильный телефон."`
-SmsBody "One time password: $passsword"
$result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен и на E-mail и на мобильный телефон."
}
catch{
$result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
}
}
elseif(![string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
# Это если нет телефона
$passsword = Get-RandomPassword -length 10
try{
Set-ADAccountPassword -Identity $chekADuser.DistinguishedName `
-NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
-Confirm:$false
Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
Send-Notification -Email $chekADuser.HomePage `
-PhoneNum $chekADuser.MobilePhone `
-EmailBody "Пароль пользователя $($chekADuser.samaccountname) был сброшен." `
-SmsBody "One time password: $passsword"
$result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на E-mail, МОБИЛЬНЫЙ НЕ УКАЗАН!!!."
}
catch{
$result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
}
}
elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and ![string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
# Это если нет Email
$passsword = Get-RandomPassword -length 10
try{
Set-ADAccountPassword -Identity $chekADuser.DistinguishedName `
-NewPassword (ConvertTo-SecureString -String $passsword -AsPlainText -Force) `
-Confirm:$false
Set-ADUser $chekADuser.DistinguishedName -ChangePasswordAtLogon $true -Confirm:$false
Send-Notification -Email $chekADuser.HomePage `
-PhoneNum $chekADuser.MobilePhone.Replace('+',"") `
-EmailBody "Hi!"`
-SmsBody "$($chekADuser.samaccountname) | one time password: $passsword"
$result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) прошла успешно, пароль отправлен на мобильный телефон, ЭЛЕКТРОННАЯ ПОЧТА НЕ УКАЗАНА!!!."
}
catch{
$result = "$(get-date): Произошла ошибка при сменен пароля у пользователя $($chekADuser.samaccountname): $($Error[0].exception.message)"
}
}
elseif([string]::IsNullOrWhiteSpace($chekADuser.HomePage) -and [string]::IsNullOrWhiteSpace($chekADuser.MobilePhone)){
# Это если нет Email и телефона
$result = "$(get-date): Cмена пароля у пользователя $($chekADuser.samaccountname) отменена, мобильный телефон и электронная почта не указаны в AD и в базе HR!!!."
}
else{
# Любая другая херня
$result = "$(get-date): Произошла ошибка при сменен пароля у пользователя необходимо уточнение данных по этому пользователю."
}
}
}
catch [System.Management.Automation.CommandNotFoundException]{
$result = "$(get-date): У вас не установлены средства управления AD! Вы точно привелигированный пользователь?? На всякий случай я отправил ваши данные в ДИБ!!!"
$whois = get-userinf
Send-Notification -Email alert@domain.site `
-PhoneNum $null `
-EmailBody "Обнаружена попытка сменить пароль пользователя $samaccountname на компьютере с отсутвующими инструментами администрирования AD!!! Возьмите 'на карандаш'!" `
-SmsBody $whois
}
return ($result + "`r`n")
}
#
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Форма
$form = New-Object System.Windows.Forms.Form
$form.Text = "Сброс пароля пользователя"
$form.Size = New-Object System.Drawing.Size(700,400)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false
# Метка
$label = New-Object System.Windows.Forms.Label
$label.Text = "Введите логин, или ФИО, или ID пользователя"
$label.Location = New-Object System.Drawing.Point(20,20)
$label.AutoSize = $true
$form.Controls.Add($label)
# Поле ввода
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(20,50)
$textBox.Size = New-Object System.Drawing.Size(300,20)
$form.Controls.Add($textBox)
# Кнопка «Сменить пароль!»
$resetButton = New-Object System.Windows.Forms.Button
$resetButton.Text = "Сменить пароль!"
$resetButton.Location = New-Object System.Drawing.Point(510,45)
$resetButton.Size = New-Object System.Drawing.Size(120,30)
$form.Controls.Add($resetButton)
# Кнопка «Проверить пользователя»
$checkButton = New-Object System.Windows.Forms.Button
$checkButton.Text = "Проверить пользователя"
$checkButton.Location = New-Object System.Drawing.Point(340,45)
$checkButton.Size = New-Object System.Drawing.Size(160,30)
$form.Controls.Add($checkButton)
# Текстовое поле для логов
$logBox = New-Object System.Windows.Forms.TextBox
$logBox.Location = New-Object System.Drawing.Point(20,100)
$logBox.Size = New-Object System.Drawing.Size(640,220)
$logBox.Multiline = $true
$logBox.ScrollBars = "Vertical"
$logBox.ReadOnly = $true
$form.Controls.Add($logBox)
# Прогрессбар ниже logBox, не налезает
$progressBar = New-Object System.Windows.Forms.ProgressBar
$progressBar.Location = New-Object System.Drawing.Point(20,340) # чуть ниже logBox
$progressBar.Size = New-Object System.Drawing.Size(640,20) # по ширине logBox
$progressBar.Style = 'Marquee'
$progressBar.Visible = $false
$form.Controls.Add($progressBar)
# Действие кнопки
$resetButton.Add_Click({
$username = $textBox.Text
if ([string]::IsNullOrWhiteSpace($username)) {
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$logBox.AppendText("$(get-date):Введите логин пользователя!`r`n")
return
}
$logBox.AppendText("$(get-date): Сброс пароля для пользователя $username...`r`n")
try {
# Вызов твоей функции
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$result = Reset-ADUserPassword -samaccountname $username
$logBox.AppendText($result + "`r`n")
}
catch {
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$logBox.AppendText("$(get-date): Ошибка: $_`r`n")
}
})
# Действие кнопки проверки пользователя
$checkButton.Add_Click({
$progressBar.Visible = $true
$form.Refresh()
$username = $textBox.Text
if ([string]::IsNullOrWhiteSpace($username)) {
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$logBox.AppendText("$(get-date): Введите логин пользователя!`r`n")
$progressBar.Visible = $false
return
}
$logBox.AppendText("$(get-date): Проверка пользователя $username...`r`n")
try {
$result = Check-User -samaccountname $username
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$logBox.AppendText($result + "`r`n")
}
catch {
$logBox.AppendText("$(get-date):* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *`r`n")
$logBox.AppendText("Ошибка: $_`r`n")
}
$progressBar.Visible = $false
})
# Показ формы
$form.Add_Shown({$form.Activate()})
[void]$form.ShowDialog()
