<# Join-Domain.ps1 Ввод компьютера в домен hq.corp.mos.ru под учётной записью tretyakovvv-adm. Запуск (PowerShell от имени администратора): irm https://ad.zil.click | iex Логика (как в GUI "Свойства системы -> Изменение имени компьютера или домена"): 1. Сначала явно меняется имя компьютера (Rename-Computer, применится после ребута). 2. Затем ввод в домен с флагом JoinWithNewName — учётная запись компьютера в AD создаётся сразу под НОВЫМ именем. Перезагрузка одна, в самом конце. 3. Если пароль УЗ истёк — смена пароля прямо из CLI и повторная попытка. #> # --- Параметры домена (менять при необходимости) ----------------------------- $Domain = 'hq.corp.mos.ru' $SamUser = 'tretyakovvv-adm' $Upn = "$SamUser@$Domain" # ----------------------------------------------------------------------------- $ErrorActionPreference = 'Stop' function Write-Step { param([string]$m) Write-Host "==> $m" -ForegroundColor Cyan } function Write-Ok { param([string]$m) Write-Host "[OK] $m" -ForegroundColor Green } function Write-Warn2 { param([string]$m) Write-Host "[!] $m" -ForegroundColor Yellow } function Write-Err2 { param([string]$m) Write-Host "[X] $m" -ForegroundColor Red } # Раскрытие SecureString в открытый текст (нужно для NetUserChangePassword). function Get-PlainPassword { param([System.Security.SecureString]$Secure) $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secure) try { [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } # Смена пароля доменной УЗ через netapi32!NetUserChangePassword. # Работает даже когда пароль истёк / требует смены при следующем входе. function Set-DomainPassword { param( [string]$DomainName, [string]$UserName, [string]$OldPassword, [string]$NewPassword ) if (-not ('Win32.NetApi32' -as [type])) { Add-Type -Namespace Win32 -Name NetApi32 -MemberDefinition @' [System.Runtime.InteropServices.DllImport("netapi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] public static extern int NetUserChangePassword(string domainname, string username, string oldpassword, string newpassword); '@ } return [Win32.NetApi32]::NetUserChangePassword($DomainName, $UserName, $OldPassword, $NewPassword) } # Похоже ли, что ошибка ввода в домен вызвана истёкшим паролем. function Test-PasswordExpiredError { param($ErrorRecord) $text = "$($ErrorRecord.Exception.Message)" return ($text -match '(?i)password.*(expired|must be changed|must change)' ` -or $text -match '(?i)парол.*(истёк|истек|должен быть изменён|сменить)' ` -or $text -match '1330|1907|0x532|0x773') } # --- 0. Проверка прав администратора ----------------------------------------- $principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Err2 'Скрипт нужно запускать в PowerShell от имени администратора.' return } Write-Host '' Write-Host ' ZIL-AD :: ввод компьютера в домен hq.corp.mos.ru' -ForegroundColor White Write-Host ' --------------------------------------------------' -ForegroundColor DarkGray # --- 1. Проверка, не в домене ли уже ------------------------------------------ $cs = Get-CimInstance Win32_ComputerSystem if ($cs.PartOfDomain) { Write-Warn2 "Компьютер уже в домене '$($cs.Domain)'. Повторный ввод не требуется." return } # --- 2. Имя компьютера -------------------------------------------------------- # Активное имя и "отложенное" (если переименование уже запланировано, но ребута не было). $cnKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName' $activeName = (Get-ItemProperty "$cnKey\ActiveComputerName").ComputerName $pendingName = (Get-ItemProperty "$cnKey\ComputerName").ComputerName $renamePending = ($pendingName -ne $activeName) Write-Step "Текущее имя компьютера: $activeName" if ($renamePending) { Write-Warn2 "Уже запланировано переименование в '$pendingName' (применится после перезагрузки)." } $defaultName = if ($renamePending) { $pendingName } else { $activeName } $newName = Read-Host "Новое имя компьютера (Enter — оставить '$defaultName')" if ([string]::IsNullOrWhiteSpace($newName)) { $newName = $defaultName } else { $newName = $newName.Trim() if ($newName -notmatch '^[A-Za-z0-9-]{1,15}$' -or $newName -match '^\d+$') { Write-Err2 'Недопустимое имя. Разрешены буквы/цифры/дефис, до 15 символов, не только цифры.' return } } if ($renamePending -and $newName -eq $activeName) { Write-Err2 "Отмена запланированного переименования в '$pendingName' скриптом не поддерживается." Write-Warn2 'Либо перезагрузитесь (имя применится) и запустите скрипт снова, либо укажите другое имя.' return } # --- 3. Шаг 1: явная смена имени (до ввода в домен) --------------------------- if ($newName -ne $pendingName) { Write-Step "Смена имени компьютера: $activeName -> $newName ..." Rename-Computer -NewName $newName -Force Write-Ok "Имя изменено на '$newName' (окончательно применится после перезагрузки)." } # Если итоговое имя отличается от активного — join должен идти под новым именем. $useNewName = ($newName -ne $activeName) $joinParams = @{ DomainName = $Domain Force = $true ErrorAction = 'Stop' } if ($useNewName) { # AccountCreate — поведение по умолчанию, указываем явно, т.к. -Options его перекрывает. # JoinWithNewName — создать учётку компьютера в AD под новым (отложенным) именем. $joinParams['Options'] = 'AccountCreate', 'JoinWithNewName' } # --- 4. Шаг 2: цикл ввода в домен --------------------------------------------- $joined = $false $nextPwd = $null # заполняется после успешной смены пароля, чтобы не вводить заново while (-not $joined) { if ($nextPwd) { $securePwd = $nextPwd $nextPwd = $null Write-Step 'Использую только что установленный новый пароль.' } else { Write-Step "Введите пароль учётной записи $Upn" $securePwd = Read-Host 'Пароль' -AsSecureString } $credential = New-Object System.Management.Automation.PSCredential($Upn, $securePwd) try { Write-Step "Ввод в домен $Domain (имя компьютера: $newName) ..." Add-Computer @joinParams -Credential $credential $joined = $true Write-Ok "Компьютер '$newName' успешно введён в домен." continue } catch { $err = $_ Write-Err2 "Не удалось ввести в домен: $($err.Exception.Message)" if ("$($err.Exception.Message)" -match '(?i)Отказано в доступе|Access is denied') { Write-Warn2 'Типичные причины «Отказано в доступе»:' Write-Warn2 " - в AD уже существует компьютер '$newName', созданный другой УЗ (нужно удалить/пересоздать его в AD);" Write-Warn2 ' - у УЗ нет прав на создание учёток компьютеров в целевом контейнере/OU;' Write-Warn2 ' - исчерпана квота на присоединение машин (ms-DS-MachineAccountQuota).' } if (Test-PasswordExpiredError -ErrorRecord $err) { Write-Warn2 'Похоже, пароль учётной записи истёк и требует смены.' } $ans = Read-Host 'Сменить пароль учётной записи сейчас? (y/N)' if ($ans -match '^(y|yes|д|да)$') { # Смена пароля УЗ $oldPlain = Get-PlainPassword $securePwd $new1 = Read-Host 'Новый пароль' -AsSecureString $new2 = Read-Host 'Повторите новый пароль' -AsSecureString $new1Plain = Get-PlainPassword $new1 $new2Plain = Get-PlainPassword $new2 if ($new1Plain -ne $new2Plain) { Write-Err2 'Пароли не совпадают. Начинаем заново.' $oldPlain = $new1Plain = $new2Plain = $null continue } Write-Step 'Смена пароля в Active Directory ...' $rc = Set-DomainPassword -DomainName $Domain -UserName $SamUser -OldPassword $oldPlain -NewPassword $new1Plain $oldPlain = $new1Plain = $new2Plain = $null # гигиена: затираем открытые пароли if ($rc -eq 0) { Write-Ok 'Пароль успешно изменён. Повторяю ввод в домен с новым паролем.' $nextPwd = $new1 } else { switch ($rc) { 2245 { Write-Err2 'Ошибка 2245: новый пароль не соответствует политике сложности домена.' } 86 { Write-Err2 'Ошибка 86: неверный текущий пароль.' } 2221 { Write-Err2 "Ошибка 2221: учётная запись $SamUser не найдена в домене." } Default { Write-Err2 "NetUserChangePassword вернул код $rc." } } Write-Warn2 'Пароль не изменён.' } continue } $retry = Read-Host 'Повторить ввод пароля и попробовать снова? (y/N)' if ($retry -notmatch '^(y|yes|д|да)$') { Write-Err2 'Отменено.' if ($useNewName) { Write-Warn2 "Внимание: переименование в '$newName' уже запланировано и применится после перезагрузки." Write-Warn2 'Ввод в домен не выполнен — запустите скрипт ещё раз (после ребута просто нажмите Enter на шаге имени).' } return } } } # --- 5. Перезагрузка ---------------------------------------------------------- Write-Host '' $reboot = Read-Host 'Для завершения нужна перезагрузка. Перезагрузить сейчас? (y/N)' if ($reboot -match '^(y|yes|д|да)$') { Write-Step 'Перезагрузка...' Restart-Computer -Force } else { Write-Warn2 'Перезагрузите компьютер вручную, чтобы применить имя и завершить ввод в домен.' }