- Проверить наличие и статус (включена/отключена) учетной записи пользователя.
- Проверить включена ли учетная запись в группу «Администраторы»
- Если учетная запись отсутствует, то создать учетную запись и добавить ее в группу администраторы, проставить флаги «Запретить смену пароля пользователем» и «Срок действия пароля не ограничен»
- Если учетная запись существует, но отключена либо не входит в группу «Администраторы», то включить учетную запись и добавить ее в группу «Администраторы», проставить флаги «Запретить смену пароля пользователем» и «Срок действия пароля не ограничен»
- Скрипт не должен зависеть от языка операционной системы.
Я подумал, что ничего сложного и первым делом решил проверить есть пользователь Admin на моем компьютере. Недолго думая, я вбил команду:
Get-User admin
И тут же получил ошибку
Get-User : Имя "Get-User" не распознано как имя командлета, функции, файла сценария или выполняемой программы.
Немного опешив, от того, что такая полезная команда и не распознала я погуглил и обнаружил, что команда Get-User работает только в консоли Powershell для Exchange. Для работы с локальными пользователями необходимо использовать Get-Localuser, а для доменных Get-Aduser. Осознав свою ошибку, я вбил:
Get-Localuser admin
И получил ответ
Name Enabled Description
---- ------- -----------
Admin True Встроенная учетная запись администратора компьютера/домена
Ну теперь проверить есть пользователь или нет, не составит большого труда, однако и тут меня ждал очередной подвох. Через условный оператор if else сделать это оказалось не так-то просто.
Дело в том, что если пользователь есть в системе, то на выход мы получаем не значение true, а целый набор данных, с именем, описанием пользователя, включена эта учетная запись или нет. А если пользователя нет, то powershell выдает ошибку. Побродив по просторам интернета, я обнаружил, что для этих целей лучше использовать оператор try сatch.
$user = 'admin'
try {
Get-LocalUser $user -ErrorAction Stop | Out-Null
write-host пользователь $user есть -foregroundcolor Green
}
Catch {
write-host пользователя $user нет -foregroundcolor Red
}
Я добавил переменную $user, чтобы было проще менять имена пользователя во всем скрипте. ErrorAction Stop необходим, чтобы скрипт не прервался на этом шаге из-за ошибки. Знак | разделяет шаги конвейера, а Out-Null скроет вывод текста ошибки. Так же я добавил вывод текста с подсветкой, для удобства проверки скрипта.
Далее я захотел проверить, включена или отключена учетная запись. Как я уже говорил, команда Get-LocalUser выдает целый набор данных и для проверки, мне нужно было выделить только один параметр Enabled со значением true или false. Для этого я воспользовался следующей командой:
(Get-LocalUser $user).enabled
Убедившись, что он работает, я сделал следующую проверку и добавил команду включения пользователя.
$user = 'admin'
if ((Get-LocalUser $user).enabled -eq "True") {
write-host учетная запись включена -foregroundcolor Green
}
else {
write-host учетная запись отключена -foregroundcolor Red
Enable-LocalUser $user
write-host учетная запись включена -foregroundcolor Green
}
Для включения пользователя, естественно необходимо запускать консоль под правами администратора.
Во втором пункте задания, необходимо было определить входит ли пользователь в группу администраторов. Для проверки наличия пользователя в той или иной группе есть команда Get-LocalGroupMember. Однако имя группы администраторов, меняется от языка операционной системы. Поэтому мне пришлось воспользоваться стандартным SID S-1-5-32-544. Проверку я также сделал через try сatch.
$user = 'admin'
try {
Get-LocalGroupMember -SID S-1-5-32-544 -Member $user -ErrorAction Stop | Out-Null
write-host пользователь $user состоит в группе администраторы -foregroundcolor Green
}
Catch {
write-host пользователь $user не состоит в группе администраторы -foregroundcolor Red
Add-LocalGroupMember -SID S-1-5-32-544 -Member $user
write-host пользователь $user состоит в группе администраторы -foregroundcolor Green
}
В случае, если пользователь не состоит в группе, администраторов он будет в нее добавлен.
На следующем этапе, у меня стояла задача определить отключена настройка смены пароля у пользователя или нет. Тут я наткнулся на очередные подводные камни. Дело в том, что если у пользователя, срок действия пароля не ограничен, то параметр PasswordExpires не выдает никаких значений. А если эта галочка отключена, то у разных пользователей будет стоять разная дата смены пароля. Выход из этого положения я всё-таки придумал:
if ((Get-LocalUser $user).PasswordExpires -eq $null) {
write-host срок действия паролья не ограничен -foregroundcolor Green
}
else {
write-host срок действия пароля ограничен -foregroundcolor Red
set-LocalUser $user -PasswordNeverExpires:$true
write-host срок действия пароля не ограничен -foregroundcolor Green
}
Дальше мне было необходимо создавать пользователя. Я никак не ожидал, что и здесь меня может ждать подвох. При создании пользователя командой
new-LocalUser -User $user -password P@ssW0rD!
Возвращается ошибка:
Не удается привязать параметр "Password". Не удается преобразовать значение "P@ssW0rD!" типа "System.String" в тип "System.Security.SecureString".
Дело в том, что параметр -password должен использовать SecureString, вместо обычной текстовой строки. Для этого я сделал переменную $password и сконвертировал её в securestring.
$password = convertto-securestring "P@ssW0rD!" -asplaintext -force
А сам скрипт по созданию пользователя и добавления его в группу администраторов
new-LocalUser -User $user -password $password -PasswordNeverExpires:$true -AccountNeverExpires:$true
Add-LocalGroupMember -SID S-1-5-32-544 -Member $user
Готовый скрипт учитывающий все условия у меня получился таким образом:
$user = 'admin'
$password = convertto-securestring "P@ssW0rD!" -asplaintext -force
try {
Get-LocalUser $user -ErrorAction Stop | Out-Null
cls
write-host пользователь $user есть -foregroundcolor Green
if ((Get-LocalUser $user).enabled -eq "True") {
write-host учетная запись включена -foregroundcolor Green
}
else {
write-host учетная запись отключена -foregroundcolor Red
Enable-LocalUser $user
write-host учетная запись включена -foregroundcolor Green
}
if ((Get-LocalUser $user).PasswordExpires -eq $null) {
write-host срок действия паролья не ограничен -foregroundcolor Green
}
else {
write-host срок действия пароля ограничен -foregroundcolor Red
set-LocalUser $user -PasswordNeverExpires:$true
write-host срок действия паролья не ограничен -foregroundcolor Green
}
try {
Get-LocalGroupMember -SID S-1-5-32-544 -Member $user -ErrorAction Stop | Out-Null
write-host пользователь $user состоит в группе администраторы -foregroundcolor Green
}
Catch {
write-host пользователь $user не состоит в группе администраторы -foregroundcolor Red
Add-LocalGroupMember -SID S-1-5-32-544 -Member $user
write-host пользователь $user состоит в группе администраторы -foregroundcolor Green
}
}
Catch {
cls
write-host пользователя $user нет -foregroundcolor Red
new-LocalUser -User $user -password $password -PasswordNeverExpires:$true -AccountNeverExpires:$true
Add-LocalGroupMember -SID S-1-5-32-544 -Member $user
write-host пользователь $user создан и добавлен в группу администраторов -foregroundcolor Green
}
Post Scriptum
Однако хранение пароля администратора в скрипте — это не лучшая практика с точки зрения безопасности. Для этого можно использовать хэш пароля, а не сам пароль.
$user = 'admin1'
$hash = "01000000d08c9ddf0115d1118c7a00c04fc297eb0100000048baf1b47a72d845b4eeda8659da9fd70000000002000000000010660000000100002000000004d2a3bf03aac92c2e172dc002d1fe8759da4655850462aface5c25f52921e05000000000e8000000002000020000000bfa652b6f7cc6845e3cee1831b54ab93dc272d0b2203024c3de8bc4789a2698a10000000923fb50dcf01125913534d0c1ba6d827400000002757df7d8a8b39c8e84b81323acff1d72419580a0022cf610926e5f081a2e895320088d482eb407f8157aad82f091abee7427b357e871d12238a8f74131238e7"
$hash
$password = ConvertTo-SecureString -String $hash
$password
new-LocalUser -User $user -password $password -PasswordNeverExpires:$true -AccountNeverExpires:$true
Add-LocalGroupMember -SID S-1-5-32-544 -Member $user
Для того, чтобы получить хэш пароля можно воспользоваться следующим скриптом
$Secure = Read-Host -AsSecureString
$Secure
$hash = ConvertFrom-SecureString -SecureString $Secure
$hash
Который запросит ввод пароля с клавиатуры.
И маленький бонус для вывода всех отключенных записей
Get-LocalUser | Where-Object {$_.enabled -eq $false}
Надеюсь, потраченное мною время поможет кому-то в работе. Да и для себя будет полезно не забыть этот бесценный опыт.
Комментарии (35)
muon
01.08.2018 07:27+2Честно говоря, не вяжется «неплохо разбираюсь в powershell» с откровениями про пайп (|). Объектный пайп — самое крутое, что есть в Powershell. И вот это
на выход мы получаем не значение true, а целый набор данных
— ожидаемое поведение для любого Get-Wtf. С ними прикольно другое, если объектов (пользователей) 2 и более, то командлет возвращает массив объектов. А если объект один, то возвращает сам объект (а не массив из одного объекта), ломая к чёртовой бабушке всю последующую логику. Рождаются костыли:
и дальше смело работаем как с массивом.$shrimps = @() $shrimps += Get-Shrimps
mayorovp
01.08.2018 08:41Можно проще:
$shrimps = @(Get-Shrimps)
Или вот так:
Get-Shrimps |% { $shrimp = $_ # ... }
Daysleeper Автор
01.08.2018 09:47Честно говоря, не вяжется «неплохо разбираюсь в powershell» с откровениями про пайп (|)
Я знал, что такое конвейер до написания этой статьи. Хотел максимально подробно разъяснить все детали, для тех пользователей, кто недавно начал работать с powershell. Но не учёл высокий уровень читателей Хабра и сейчас откровенно жалею за потраченное мной время на написание статьи.
С ними прикольно другое, если объектов (пользователей) 2
В моих примерах везде явно задан конкретный пользовать. Поэтому возвращать будет либо информацию о конкретном пользователе, либо ошибку если пользователя в системе отсутсвует.
Массив данных можно получить только с помощью команды из дополненияGet-LocalUser | Where-Object {$_.enabled -eq $false}
В любом случае, спасибо за уточнение.
SAGSa
01.08.2018 09:57По умолчанию ваш скрипт будет работать только на windows server 2016 и windows 10 версии выше 1607, так как модуль Microsoft.PowerShell.LocalAccounts в котором содержится командлет Get-Localuser есть только на этих системах. Наверное для решения задачи лучше было попробовать использовать wmi.
Daysleeper Автор
01.08.2018 10:06Спасибо за уточнение. Я действительно забыл уточнить что писал на powershell 5.1
zigrus
01.08.2018 10:18выполнил Get-Localuser в Win7
все получилосьSAGSa
01.08.2018 10:21Значит у вас установлен powershell 5.1 либо модуль Microsoft.PowerShell.LocalAccounts.
Daysleeper Автор
01.08.2018 10:22Проверьте версию powershell на своей системе.
$psversiontable.psversion
Возможно на системе powershell обновлен.
zigrus
01.08.2018 10:06-2как можно средствами powershell создать почтовые ящики пользователям на exchange 2003
Daysleeper Автор
01.08.2018 10:09насколько я знаю, Exchange начал поддерживать powershell с версии 2010.
zigrus
01.08.2018 10:10есть там powershell
Daysleeper Автор
01.08.2018 12:55на Хабре пишут, что Exchange Management Shell поддерживает с 2007 habr.com/post/130640
Hikedaya
01.08.2018 22:08Например, вот так:
Function CreateNewUserMailbox($objUser, $MakeHidden, $mdb) { $CreateNewUserMailbox = $False $objUser.CreateMailbox($mdb) $objUser.Put("msExchUserAccountControl", 0) $objUser.SetInfo() if ($MakeHidden) { $objUser.Put("msExchHideFromAddressLists", $MakeHidden) $objUser.SetInfo() } } $userDN = (get-qaduser $userLogin).directoryentry.distinguishedname $user = [adsi]"LDAP://$userDN" $mdbName = "CN=Store1,CN=SG1,CN=InformationStore,CN=EXCH-01,CN=Servers,CN=Org,CN=Administrative Groups,CN=Org,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=example,DC=com"} CreateNewUserMailbox $user $false $mdbName
Пусть не смущает командлет Get-QADUser — в те времена я использовал PowerGUI.
paranoya_prod
01.08.2018 12:00<Зануда on>
Зачем проверять значение «срок действия пароля не ограничен», если его всё равно нужно устанавливать? Этим мы сократим код скрипта и время выполнения. :)
То же самое и с группой, а ошибку вторичного добавления юзера в группу можно просто игнорировать.Daysleeper Автор
01.08.2018 12:33Если в АД будет необходимость проверить у кого из пользователей стоит смена пароля, а у кого нет, без принудительного изменения этого параметра, мой пример будет полезен.
Не стоит воспринимать готовый скрипт как универсальный и на все случаи жизни. Моя цель была показать несколько примеров, которые можно будет в будущем использовать под свои конкретные задачи.
Busla
01.08.2018 12:34+1странные у вас «просторы интернета», наличие/отсуствие объекта элементарно проверяется в if:
if (Get-LocalUser 'admin' -ErrorAction SilentlyContinue) { Write-Host 'Есть пользователь'} else {Write-Host 'Нет пользователя' }
SAGSa
01.08.2018 13:22+4Все таки правильнее обрабатывать ошибки а не скрывать их. В вашем случае скрипт будет выдавать «нет пользователя» если произойдет любая ошибка в Get-LocalUser и он ничего не вернет. Использование try{}catch{}, более оправдано… Правильнее было бы
$user="admin" try { Get-LocalUser $user -ErrorAction Stop write-host пользователь $user есть -foregroundcolor Green } Catch { if ($_.FullyQualifiedErrorId -eq "UserNotFound,Microsoft.PowerShell.Commands.GetLocalUserCommand") { write-host "Пользователь $User не найден" -foregroundcolor Red } else { Write-Error $_ } }
mixeon
01.08.2018 14:07Нуу… нет, все можно было сделать намного проще и понятнее. Вместо try/catch вполне удобно использовать параметр -ErrorAction. А «хэш» пароля не будет корректно декодироваться под другой учетной записью (другом пк). Чтобы он «работал» везде, нужно использовать параметр key. Самый простой вариант, это запросить прямо из консоли новый пароль. И по итогу весь скрипт будет выглядеть примерно так:
$userName = "Admin" if (-not (Get-LocalUser $userName -ErrorAction SilentlyContinue)) { #Если У/З отсутствует, то будет создана $pass = Read-Host "Inpur new password for $userName" -AsSecureString New-LocalUser -Name $userName -Password $pass -UserMayNotChangePassword -PasswordNeverExpires } #Раз У/З есть (а если не было, то уже есть), то можем создать объект аккаунта, с ним удобнее $Account = Get-LocalUser $userName #Включаем аккаунт, если отключен if (-not $Account.Enabled) { Enable-LocalUser $Account } #Если аккаунт не входит в группу администраторов, то добавим if (-not (Get-LocalGroupMember -Member $Account -SID S-1-5-32-544 -ErrorAction SilentlyContinue )) { Add-LocalGroupMember -SID S-1-5-32-544 -Member $Account } #Задаем параметры «Запретить смену пароля пользователем» и «Срок действия пароля не ограничен» Set-LocalUser $Account -PasswordNeverExpires $true -UserMayChangePassword $false
Xeonkeeper
01.08.2018 17:28Запретить смену пароля текущим членам группы администраторов невозможно — ругается.
Пришлось сначала удалять, прописывать это свойство, а потом заново добавлять. Сделал вообще без проверок и с глобальным игнором ошибок.
$ErrorActionPreference = 'SilentlyContinue' $username = "Admin" $AdminGroup = "S-1-5-32-544" $getuser = Get-LocalUser $username if ($getuser -eq $null) { $Password = Read-Host -AsSecureString New-LocalUser -Name $username ` -PasswordNeverExpires ` -UserMayNotChangePassword ` -Password $Password Add-LocalGroupMember -SID $AdminGroup -Member $username } else { Remove-LocalGroupMember -Member $username -SID $AdminGroup Set-LocalUser -Name $username ` -PasswordNeverExpires $true ` -UserMayChangePassword $false ` -Password $Password Add-LocalGroupMember -SID $AdminGroup -Member $username Enable-LocalUser $username }
Daysleeper Автор
01.08.2018 17:36консоль под админом запускаете?
только что перепроверил
set-LocalUser admin -PasswordNeverExpires:$true
галочка по сроку действую пароля устанавливается.Xeonkeeper
01.08.2018 17:44С этим проблем нет, проблемы есть со свойством смены пароля, а не его сроком действия.
попробуйте
Set-LocalUser -Name admin -UserMayChangePassword $false
Daysleeper Автор
01.08.2018 18:05Да, действительно ругается, на одного администратора в системе.
Попробуйте через конвейер это сделать.
$password = convertto-securestring "P@ssW0rD!" -asplaintext -force Set-LocalUser -Name admin -Password $Password | Set-LocalUser -UserMayChangePassword $false
Xeonkeeper
01.08.2018 18:11А что собственно вы пытаетесь передать по этому пайпу?
PS, не выйдет никак. Даже через GUI он ругается на ограничения. Возможно этот нюанс тоже повлиял на оценку тестового задания.Daysleeper Автор
01.08.2018 18:15может сначала ответите, у вас сработало или нет?
Xeonkeeper
01.08.2018 18:23+1Я специально для вас проверил заведомо не рабочее решение и нет, оно не сработало.
Daysleeper Автор
01.08.2018 18:22да, действительно. даже через консоль этот параметр не изменить, если в системе остался только один администратор.
Daysleeper Автор
Кстати, сегодня узнал, что в работе мне отказали :( Готов выслать резюме в личку, если кого интересует в Питере.