среда, 23 ноября 2016 г.

PowerShell: скрипт с графическим интерфейсом для восстановления связи рассинхронизированных компьютеров с аккаунтами в Acitve Directory

Объекты Active Directory - компьютеры, так же как и пользовательские аккаунты, имеют собственный внутренний пароль, который периодически изменяется согласно политикам безопасности домена. Таким образом, если машина была восстановлена из снимка или бэкапа, может получится ситуация, при которой пароли внутри компьютера и в Active Directory окажутся рассинхронизированными. Для устранения проблемы достаточно вывести и завести машину обратно в домен или выполнить команду PowerShell:

Test-ComputerSecureChannel -Repair

Автоматизировать данную процедуру поможет следующий скрипт, который позволяет, используя удобный графический интерфейс, удалённо вывести-ввести компьютер в домен. Понадобится указать данные доменной учётной записи с правом добавления машин в Active Directory и учётной записи локального администратора целевого компьютера. Работает скрипт по WMI.


Скрипт обязательно сохранять в UTF-8 кодировке:

$HideWindow = '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);'
Add-type -Name win -Member $HideWindow -Namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
Add-Type -assembly System.Windows.Forms
Import-Module ActiveDirectory

Function OnLoad()
{
 $global:comps = Get-ADComputer -Filter * -Properties Name,OperatingSystem
 $listbox1.Items.Clear()
 foreach ($Item in $global:comps)
  {
   $listbox1.Items.Add($Item.Name)
  }
}

Function listbox1_SelectedIndexChanged()
{
 $label1.Text = ($global:comps | Where-Object {$_.Name -like $listbox1.SelectedItem}).OperatingSystem
}

Function button1_Click()
{
 if ($listbox1.SelectedItem) {
  $compName = $listbox1.SelectedItem
 }
 else {
  $compName = $textbox1.Text
 }
 $user = Get-WMIObject -class Win32_ComputerSystem | Select username
 $cred = Get-Credential $user.username -Message "Введите данные пользователя с правами Администратора домена:"
 $Computer = Get-WmiObject Win32_ComputerSystem -ComputerName $compName -Credential $cred -ErrorVariable myerror
 if ($myerror) {
  $myerror.clear()
  $localstring = $compName + "\"
  $localcred = Get-Credential $localstring -Message "Введите данные пользователя, являющимся локальным администратором на удаленном компьютере:"
  $Computer = Get-WmiObject Win32_ComputerSystem -ComputerName $compName -Credential $localcred
   if ($Computer)
   {
   $domain = $Computer.Domain
   $r = $Computer.UnjoinDomainOrWorkGroup($localcred.GetNetworkCredential().Password, $localcred.UserName, 0)
   if ($r.ReturnValue -eq "0"){
    $Computer = Get-WmiObject Win32_ComputerSystem -ComputerName $compName -Credential $localcred
    $r = $Computer.JoinDomainOrWorkGroup($domain, $cred.GetNetworkCredential().Password, $cred.UserName, 0, 1)
    if ($r.ReturnValue -eq "0"){
     Sleep(3)
     $winOS = Get-WmiObject Win32_OperatingSystem -ComputerName $compName -Credential $localcred
     $sresult = $winOS.win32shutdown(6)
     if($sresult.ReturnValue -ne "0"){
      [System.Windows.Forms.MessageBox]::Show("Не удалось перегрузить компьютер. Ошибка №" + $sresult.ReturnValue, "Ошибка")
     }
    }
    else {
     [System.Windows.Forms.MessageBox]::Show("Не удалось ввести компьютер в домен. Ошибка №" + $r.ReturnValue, "Ошибка")
    }
   }
   if ($r.ReturnValue -eq "1326")
   {[System.Windows.Forms.MessageBox]::Show("Неправильное имя пользователя или пароль.", "Ошибка")}
   elseif ($r.ReturnValue -eq "2221")
   {[System.Windows.Forms.MessageBox]::Show("Компьютер не найден.", "Ошибка")}
   elseif ($r.ReturnValue -eq "0")
   {$OUTPUT = [System.Windows.Forms.MessageBox]::Show("У клиента " + $compName + " восстановлены доверительные отношения с доменом.", "Готово!")
   $textbox1.Text = ""
   OnLoad
  }
   else {[System.Windows.Forms.MessageBox]::Show("Номер ошибки: " + $r.ReturnValue, "Ошибка")}
  }
  else {[System.Windows.Forms.MessageBox]::Show("Не могу подключиться к компьютеру " + $compName, "Ошибка")
  }
 }
 else {
  [System.Windows.Forms.MessageBox]::Show("Компьютер " + $compName + " корректно функционирует в домене.", "Ошибка")
 }
}

Function textbox1_KeyUp()
{
 if ($_.KeyCode -eq "Enter") {
  button1_Click
  return
 }
 if ($_.KeyCode -eq "Down") {
  $listbox1.SelectedIndex++
  listbox1_SelectedIndexChanged
  return
 }
 if ($_.KeyCode -eq "Up") {
  $listbox1.SelectedIndex--
  listbox1_SelectedIndexChanged
  return
 }
 $listbox1.Items.Clear()
 $filtered_comps = $global:comps | Where-Object {$_.Name -like $textbox1.Text + '*'}
 foreach ($Item in $filtered_comps)
 {
  $listbox1.Items.Add($Item.Name)
  
 }
 $listbox1.SelectedIndex = 0
 listbox1_SelectedIndexChanged
}

$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "Восстановление связи в AD"
$MainForm.Width = 360
$MainForm.Height = 260
$MainForm.StartPosition = "CenterScreen"
$MainForm.AutoSize = $false

$textbox1 = New-Object System.Windows.Forms.TextBox
$textbox1.Location  = New-Object System.Drawing.Point(0,0)
$textbox1.Width = 120
$textbox1.Height = 20
$textbox1.Text = ''
$textbox1.Add_KeyUp({textbox1_KeyUp})
$MainForm.Controls.Add($textbox1)

$label1 = New-Object System.Windows.Forms.Label
$label1.Text = ""
$label1.Location = New-Object System.Drawing.Point(0,20)
$label1.Width = 165
$label1.Height = 30
$MainForm.Controls.Add($label1)

$button1 = New-Object System.Windows.Forms.Button
$button1.Text = 'Восстановить связь'
$button1.Location = New-Object System.Drawing.Point(165,0)
$button1.Width = 180
$button1.Height = 20
$button1.Add_Click({button1_Click})
$MainForm.Controls.Add($button1)

$listbox1 = New-Object System.Windows.Forms.ListBox
$listbox1.Location  = New-Object System.Drawing.Point(0,60)
$listbox1.Width = 345
$listbox1.Height = 200
$listbox1.Add_Click({listbox1_SelectedIndexChanged})
$listbox1.Add_KeyUp({listbox1_SelectedIndexChanged})
$MainForm.Controls.add($listbox1)

OnLoad
$MainForm.ShowDialog()

среда, 9 ноября 2016 г.

VMware PowerCLI: находим виртуальную машину в инфраструктуре по её имени хоста гостевой ОС

Если возникла необходимость отыскать виртуальный хост с определённым именем компьютера внутри ОС, воспользуйтесь следующими командлетами PowerCLI:

Get-VM | where {$_.Guest.HostName -match "MYVIRTUALPC"} | select Name

пятница, 4 ноября 2016 г.

PowerShell: скрипт с графическим интерфейсом для подключения с помощью теневого сеанса ("Remote Desktop Shadow") к удалённым компьютерам в домене Active Directory

Данная утилита позволяет с удобством находить компьютеры в Active Directory и осуществлять подключение к консольным или RDP-сессиям пользователей с помощью теневого сеанса ("Remote Desktop Shadow"). Для ОС Windows ниже версии 8.1/2012 R2 будет использован стандартный "Удалённый помощник". Подключение происходит как в режиме просмотра, так и в режиме управления сеансом.


Скрипт обязательно сохранять в UTF-8 кодировке:

$HideWindow = '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);'
Add-type -Name win -Member $HideWindow -Namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
Add-Type -assembly System.Windows.Forms
Import-Module ActiveDirectory

Function OnLoad()
{
 $global:comps = Get-ADComputer -Filter * -Properties Name,OperatingSystem
 $listbox1.Items.Clear()
 foreach ($Item in $global:comps)
  {
   $listbox1.Items.Add($Item.Name)
  }
}

Function listbox1_SelectedIndexChanged()
{
 $label1.Text = ($global:comps | Where-Object {$_.Name -like $listbox1.SelectedItem}).OperatingSystem
}

Function listbox1_KeyUp()
{
 listbox1_SelectedIndexChanged
 if ($_.KeyCode -eq "Enter") {
  button1_Click
  return
 }
}

Function button1_Click()
{
 if ($listbox1.SelectedItem) {
  $compName = $listbox1.SelectedItem
 }
 else {
  $compName = $textbox1.Text
 }
 $os = Get-WmiObject -Computer $compName -Class Win32_OperatingSystem -ErrorVariable myerror
 if ($myerror) {
  [System.Windows.Forms.MessageBox]::Show($myerror.Exception.Message, "Ошибка")
  $myerror.clear()
  return
 }
 if ($os.Version.StartsWith("10") -or $os.Version.StartsWith("6.3")) {
  $regpath = "\\"+$compName+"\HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
  reg add $regpath /v Shadow /t REG_DWORD /d 2 /f
  $queryResults = (qwinsta /server:$compName | foreach { (($_.trim() -replace "\s+",","))})
  $sessionuserarray = @()
  $sessionidarray = @()
  foreach ($queryResult in $queryResults){
   if($queryResult.Split(",")[0] -eq "console" -and !$checkbox2.Checked){
    $global:sessionid = $queryResult.Split(",")[2]
   }
   elseif ($queryResult.Split(",")[0] -like "rdp-tcp#*" -and $checkbox2.Checked) {
    $sessionuserarray += $queryResult.Split(",")[1]
    $sessionidarray += $queryResult.Split(",")[2]
   }
  }
  if($global:sessionid -and !$checkbox2.Checked) {
   Connect
  }
  elseif ($sessionidarray -and $checkbox2.Checked) {
   $DialogForm = New-Object System.Windows.Forms.Form
   $DialogForm.Text = "Выбор удаленной сессии"
   $DialogForm.Width = 340
   $DialogForm.Height = 170
   $DialogForm.StartPosition = "CenterScreen"
   $DialogForm.AutoSize = $false
   
   $listboxDialog = New-Object System.Windows.Forms.ListBox
   $listboxDialog.Location  = New-Object System.Drawing.Point(0,0)
   $listboxDialog.Width = 325
   $listboxDialog.Height = 100
   $listboxDialog.Add_Click({listboxDialog_SelectedIndexChanged})
   $listboxDialog.Add_KeyUp({listboxDialog_SelectedIndexChanged})
   foreach ($ruser in $sessionuserarray) {
    $listboxDialog.Items.Add($ruser)
   }
   $DialogForm.Controls.Add($listboxDialog)
   
   $buttonDialog = New-Object System.Windows.Forms.Button
   $buttonDialog.Text = 'ОК'
   $buttonDialog.Location = New-Object System.Drawing.Point(90,100)
   $buttonDialog.Width = 160
   $buttonDialog.Height = 20
   $buttonDialog.Add_Click({buttonDialog_Click})
   $DialogForm.Controls.Add($buttonDialog)
   
   $DialogForm.ShowDialog()
  }
  else {
   if ($checkbox2.Checked) {
    [System.Windows.Forms.MessageBox]::Show("Невозможно идентифицировать RDP-сессию.", "Ошибка")
   }
   else {
   [System.Windows.Forms.MessageBox]::Show("Невозможно идентифицировать консольную сессию.", "Ошибка")
   }
  }
  Sleep(3)
  reg delete $regpath /v Shadow /f
 }
 else {
  msra /offerRA $compName
 }
}

Function listboxDialog_SelectedIndexChanged()
{
 $iter = 0
 foreach ($remuser in $sessionuserarray) {
  if ($remuser -like $listboxDialog.SelectedItem) {
   $global:sessionid = $sessionidarray[$iter]
  }
  $iter++
 }
}

Function buttonDialog_Click()
{
 Connect
 $DialogForm.Close()
}

Function textbox1_KeyUp()
{
 if ($_.KeyCode -eq "Enter") {
  button1_Click
  return
 }
 if ($_.KeyCode -eq "Down") {
  $listbox1.SelectedIndex++
  listbox1_SelectedIndexChanged
  return
 }
 if ($_.KeyCode -eq "Up") {
  $listbox1.SelectedIndex--
  listbox1_SelectedIndexChanged
  return
 }
 $listbox1.Items.Clear()
 $filtered_comps = $global:comps | Where-Object {$_.Name -like $textbox1.Text + '*'}
 foreach ($Item in $filtered_comps)
 {
  $listbox1.Items.Add($Item.Name)
 }
 $listbox1.SelectedIndex = 0
 listbox1_SelectedIndexChanged
}

Function Connect()
{
 if($checkbox1.Checked){
  mstsc /shadow:$global:sessionid /v:$compName /noConsentPrompt
 }
 else {
  mstsc /shadow:$global:sessionid /v:$compName /control /noConsentPrompt
 }
}

$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "Удаленный теневой сеанс"
$MainForm.Width = 360
$MainForm.Height = 345
$MainForm.StartPosition = "CenterScreen"
$MainForm.AutoSize = $false

$textbox1 = New-Object System.Windows.Forms.TextBox
$textbox1.Location  = New-Object System.Drawing.Point(0,0)
$textbox1.Width = 120
$textbox1.Height = 20
$textbox1.Text = ''
$textbox1.Add_KeyUp({textbox1_KeyUp})
$MainForm.Controls.Add($textbox1)

$button1 = New-Object System.Windows.Forms.Button
$button1.Text = 'Подключиться'
$button1.Location = New-Object System.Drawing.Point(165,0)
$button1.Width = 180
$button1.Height = 20
$button1.Add_Click({button1_Click})
$MainForm.Controls.Add($button1)

$checkbox1 = New-Object System.Windows.Forms.Checkbox
$checkbox1.Location  = New-Object System.Drawing.Point(165,20)
$checkbox1.Width = 180
$checkbox1.Height = 30
$checkbox1.Text = "Только просмотр"
$MainForm.Controls.Add($checkbox1)

$checkbox2 = New-Object System.Windows.Forms.Checkbox
$checkbox2.Location  = New-Object System.Drawing.Point(165,50)
$checkbox2.Width = 180
$checkbox2.Height = 30
$checkbox2.Text = "Подключаться к сеансам RDP"
$MainForm.Controls.Add($checkbox2)

$label1 = New-Object System.Windows.Forms.Label
$label1.Text = ""
$label1.Location = New-Object System.Drawing.Point(0,20)
$label1.Width = 165
$label1.Height = 90
$MainForm.Controls.Add($label1)

$listbox1 = New-Object System.Windows.Forms.ListBox
$listbox1.Location  = New-Object System.Drawing.Point(0,110)
$listbox1.Width = 345
$listbox1.Height = 200
$listbox1.Add_Click({listbox1_SelectedIndexChanged})
$listbox1.Add_KeyUp({listbox1_KeyUp})
$MainForm.Controls.add($listbox1)

OnLoad
$MainForm.ShowDialog()