Принцип предоставления минимальных прав в применении к Active Directory (AD) означает, что пользователям следует назначать только те разрешения, которые необходимы для выполнения их профессиональных обязанностей. Чем крупнее компания, тем больше вероятность, что разрешения AD даются различным группам. Типичный пример — предоставление разрешений группе службы поддержки для сброса паролей и разблокирования учетных записей пользователей. Дополнительные сведения о делегировании AD можно найти в тематическом разделе Delegating administra­tion («Делегирование админи­стри­рования») документации по продукту по адресу: https://technet.microsoft.com/en-us/library/cc778807.aspx.

Принцип предоставления минимальных прав также применяется для управления учетными записями компьютеров. По умолчанию пользователи домена могут создать и присоединить к домену до 10 компьютеров. Это значение в домене можно изменить с помощью атрибута ms-DS-MachineAccountQuota, как отмечается в статье базы знаний Microsoft Default limit to number of workstations a user can join to the domain (https://support.microsoft.com/en-us/kb/243327). Многие администраторы доменов устанавливают значение этого параметра равным нулю, чтобы обеспечить соответствие процессам и стандартам компании (например, запретить пользователям назначать компьютерам произвольные имена). В результате во многих компаниях требуется делегировать права для присоединения компьютеров к домену.

Предоставление разрешения на присоединение компьютеров к домену

Вы можете назначить разрешения на присоединение компьютера при создании учетной записи компьютера, нажав кнопку Change («Изменить») в обычном графическом интерфейсе Microsoft (см. метку 1 на экранах 1 и 2).

 

Создание новой учетной записи компьютера в оснастке Active Directory Users and Computers (ADUC)
Экран 1. Создание новой учетной записи компьютера в оснастке Active Directory Users and Computers (ADUC)

 

Создание новой учетной записи компьютера с помощью Active Directory Administrative Center (ADAC)
Экран 2. Создание новой учетной записи компьютера с помощью Active Directory Administrative Center (ADAC)

С помощью кнопки Change в средствах ADUC и ADAC графического интерфейса вы предоставляете набор разрешений на объект-компьютер. Далее я покажу, каким образом можно назначить разрешения вручную, а затем мы рассмотрим сценарий PowerShell для автоматизации процесса.

Предоставление разрешения на присоединение вручную с помощью графического интерфейса

Ниже последовательно описан процесс предоставления разрешений на присоединение учетной записи компьютера с консоли ADUC вручную.

  1. В меню View («Вид») выберите режим Advanced Features («Дополнительные параметры»). В противном случае вкладка Security («Безопасность») для объектов AD не будет видна.
  2. Дважды щелкните объект-компьютер, чтобы увидеть его свойства, а затем выберите вкладку Security.
  3. Нажмите кнопку Add («Добавить»), выберите пользователя или группу, которые смогут присоединить компьютер, а затем установите в столбце Allow («Разрешить») флажки Reset password («Сбросить пароль»), Validated write to DNS host name («Удостоверенная запись на узел с DNS-именем»), Validated write to service principal name («Удостоверенная запись на узел с именем участника службы») и Write account restrictions («Ограничения учетной записи для записи»), как показано на экране 3. Нажмите кнопку Apply («Применить»).

 

Назначение разрешений на подсоединение компьютера к домену в графическом интерфейсе
Экран 3. Назначение разрешений на подсоединение компьютера к домену в графическом интерфейсе

Ручное назначение разрешений на присоединение компьютера с помощью кнопки Change или вкладки Security — медленный процесс с высокой вероятностью ошибок. Поэтому давайте посмотрим, как можно его автоматизировать с помощью сценария PowerShell.

Сценарий Grant-ComputerJoinPermission.ps1

Я подготовил сценарий Grant-ComputerJoinPermission.ps1 (см. листинг), чтобы предоставить четыре необходимых разрешения (см. экран 3) для одной или нескольких учетных записей компьютеров. Синтаксис сценария следующий:

Grant-ComputerJoinPermission
  [-Identity] identity [-Name]
  computername [-Domain domain]
  [-Credential credential] [-WhatIf] [-Confirm]

Параметр -Identity указывает, кто сможет присоединять компьютеры к домену. Вы можете ввести этот параметр в формате username (например, kendyer), domain\username (например, fabrikam\kendyer) или username@domain (например, kendyer@fabrikam.com). Имя параметра (-Identity) вводить не обязательно, если вы указываете его первым в командной строке. Этот параметр не поддерживает подстановочные знаки.

Параметр -Name задает имена одной или нескольких учетных записей компьютеров. Имя параметра (-Name) можно опустить, если разместить имена учетных записей компьютеров на второй позиции в командной строке сценария. Этот параметр принимает входные данные конвейера, поэтому имя параметра тоже следует опустить, если имена компьютеров передаются в сценарий через конвейер. Данный параметр не поддерживает подстановочные знаки.

Параметр -Domain указывает имя домена, в котором размещаются учетные записи компьютеров (например, fabrikam или fabrikam.com).

Параметр -Credential указывает учетные данные, имеющие право назначать разрешения учетным записям компьютеров. Вы можете использовать этот параметр, если выполнили вход с учетной записью, не имеющей достаточных прав для предоставления разрешений учетным записям компьютеров.

Поведение параметров -WhatIf и -Confirm — такое же, как у команд PowerShell: -WhatIf указывает, какие действия выполняет сценарий, в то время как эти действия не совершаются, а -Confirm запрашивает подтверждение перед выполнением любого действия.

Примеры команд

1. Предоставить учетной записи kendyer разрешение для присоединения компьютера pc1 к домену:

Grant-ComputerJoinPermission kendyer pc1

2. Предоставить учетной записи kendyer разрешение для присоединения списка компьютеров к домену:

Get-Content Computers.txt |
  Grant-ComputerJoinPermission
  kendyer -Verbose

В этом примере файл Computers.txt содержит список имен компьютеров (по одному на строку). PowerShell покажет вносимые изменения из-за параметра -Verbose.

3. Предоставить учетной записи kendyer разрешение для присоединения компьютера pc1 к домену с использованием альтернативных учетных данных:

Grant-ComputerJoinPermission
  kendyer pc1-Credential (Get-Credential)

В этом примере команда Get-Credential запрашивает учетные данные, и PowerShell использует учетные данные, введенные в параметре -Credential (скобки вокруг Get-Credential обязательны).

4. Создать новую учетную запись компьютера и предоставить учетной записи kendyer разрешение для присоединения к домену:

New-ADComputer SALES1-Path
  "OU=Sales, DC=fabrikam, DC=com"
  -PassThru |
Grant-ComputerJoinPermission kendyer

В этом примере команда New-ADComputer создает объект-компьютер и выводит его с использованием параметра -PassThru. Затем объект-компьютер выводится в сценарий Grant-ComputerJoinPermission.ps1, который предоставляет учетной записи kendyer разрешение для присоединения компьютера к домену.

5. Создать список учетных записей компьютеров в подразделении и предоставить учетной записи lynndyer разрешение на их присоединение к домену:

Get-Content Computers.txt |
  ForEach-Object {
New-ADComputer $_ -Path
  "OU=Sales, DC=fabrikam, DC=com"
  -PassThru
} | Grant-ComputerJoinPermission lynndyer

В этом примере файл Computers.txt содержит список имен компьютеров, по одному в строке.

Автоматизация предоставления разрешения на присоединение

Итак, отныне предоставление разрешения на присоединение компьютеров к домену не обязательно медленный и подверженный ошибкам процесс графического интерфейса. Теперь вы можете автоматизировать его в PowerShell с помощью сценария Grant-ComputerJoinPermission.ps1.

Листинг. Сценарий автоматизации предоставления разрешений на подсоединение компьютеров к домену
# Grant-ComputerJoinPermission.ps1
# Written by Bill Stewart (bstewart@iname.com)
#
# Grants an AD identity the ability to join one or more computers to the
# domain.
#requires -version 2
<#
.SYNOPSIS
Grants an AD identity permission to join one or more computers to a domain.
.DESCRIPTION
Grants an AD identity permission to join one or more computers to a domain. The identity is granted the following four permissions over each computer account:
  * Reset password
  * Validated write to DNS host name
  * Validated write to service principal name
  * Write account restrictions
.PARAMETER Identity
Identity of the account (user or group) to be granted join permission, in sAMAccountName format (e.g., 'KenDyer' or 'FABRIKAM\KenDyer'). Wildcards are not permitted.
.PARAMETER Name
Specifies one or more computer names. Wildcards are not permitted.
.PARAMETER Domain
Specifies the domain name where the computer(s) reside (e.g., 'FABRIKAM', 'fabrikam.com', or 'DC=fabrikam,DC=com').
.PARAMETER Server
Specifies a domain server where the AD permissions should be set.
.PARAMETER Credential
Specifies credentials that have permission to update the permissions of the computer account(s).
.INPUTS
Strings or objects with a Name property. AD computer objects can be used as inputs because they have a Name property.
.OUTPUTS
No output.
.EXAMPLE
PS C:\> Grant-ComputerJoinPermission KenDyer COMPUTER1
Grants the KenDyer account permission to join the computer COMPUTER1 to the domain.
.EXAMPLE
PS C:\> Grant-ComputerJoinPermission KenDyer COMPUTER1 -Credential (Get-Credential)
Grants the KenDyer account permission to join the computer COMPUTER1, but prompts for credentials to allow you to specify an account with sufficient authority to make the AD permission changes.
.EXAMPLE
PS C:\> Get-ADComputer -Filter { Name -like "COMPUTER?" } | Grant-ComputerJoinPermission FredDyer
Grants the FredDyer account permission to join the computers matching the wildcard pattern to the domain. This example shows how to use wildcards even though the -Name parameter doesn't support them.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
  [parameter(Position=0,Mandatory=$true)]
    [Security.Principal.NTAccount] $Identity,
  [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true,
  ValueFromPipelineByPropertyName=$true)]
  [alias("ComputerName")]
    [String[]] $Name,
    [String] $Domain,
    [String] $Server,
    [Management.Automation.PSCredential] $Credential
)
begin {
  # Validate if identity exists
  try {
    [Void] $identity.Translate([Security.Principal.SecurityIdentifier])
  }
  catch [Security.Principal.IdentityNotMappedException] {
    throw "Unable to identify identity - '$identity'"
  }
  # Create DirectorySearcher object
  $Searcher = [ADSISearcher] ""
  # Initializes DirectorySearcher object
  function Initialize-DirectorySearcher {
    [Void] $Searcher.PropertiesToLoad.Add("distinguishedName")
    if ( $Domain ) {
      if ( $Server ) {
        $path = "LDAP://$Server/$Domain"
      }
      else {
        $path = "LDAP://$Domain"
      }
    }
    else {
      if ( $Server ) {
        $path = "LDAP://$Server"
      }
      else {
        $path = ""
      }
    }
    if ( $Credential ) {
      $networkCredential = $Credential.GetNetworkCredential()
      $dirEntry = New-Object DirectoryServices.DirectoryEntry(
        $path,
        $networkCredential.UserName,
        $networkCredential.Password
      )
    }
    else {
      $dirEntry = [ADSI] $path
    }
    $Searcher.SearchRoot = $dirEntry
    $Searcher.Filter = "(objectClass=domain)"
    try {
      [Void] $Searcher.FindOne()
    }
    catch [Management.Automation.MethodInvocationException] {
      throw $_.Exception.InnerException
    }
  }
  Initialize-DirectorySearcher
  # AD rights GUIDs
  $AD_RIGHTS_GUID_RESET_PASSWORD      = "00299570-246D-11D0-A768-00AA006E0529"
  $AD_RIGHTS_GUID_VALIDATED_WRITE_DNS = "72E39547-7B18-11D1-ADEF-00C04FD8D5CD"
  $AD_RIGHTS_GUID_VALIDATED_WRITE_SPN = "F3A64788-5306-11D1-A9C5-0000F80367C1"
  $AD_RIGHTS_GUID_ACCT_RESTRICTIONS   = "4C164200-20C0-11D0-A768-00AA006E0529"
  # Searches for a computer object; if found, returns its DirectoryEntry
  function Get-ComputerDirectoryEntry {
    param(
      [String] $name
    )
    $Searcher.Filter = "(&(objectClass=computer)(name=$name))"
    try {
      $searchResult = $Searcher.FindOne()
      if ( $searchResult ) {
        $searchResult.GetDirectoryEntry()
      }
    }
    catch [Management.Automation.MethodInvocationException] {
      Write-Error -Exception $_.Exception.InnerException
    }
  }
  function Grant-ComputerJoinPermission {
    param(
      [String] $name
    )
    $domainName = $Searcher.SearchRoot.dc
    # Get computer DirectoryEntry
    $dirEntry = Get-ComputerDirectoryEntry $name
    if ( -not $dirEntry ) {
      Write-Error "Unable to find computer '$name' in domain '$domainName'" -Category ObjectNotFound
      return
    }
    if ( -not $PSCmdlet.ShouldProcess($name, "Allow '$identity' to join computer to domain '$domainName'") ) {
      return
    }
    # Build list of access control entries (ACEs)
    $accessControlEntries = New-Object Collections.ArrayList
    #--------------------------------------------------------------------------
    # Reset password
    #--------------------------------------------------------------------------
    [Void] $accessControlEntries.Add((
      New-Object DirectoryServices.ExtendedRightAccessRule(
        $identity,
        [Security.AccessControl.AccessControlType] "Allow",
        [Guid] $AD_RIGHTS_GUID_RESET_PASSWORD
      )
    ))
    #--------------------------------------------------------------------------
    # Validated write to DNS host name
    #--------------------------------------------------------------------------
    [Void] $accessControlEntries.Add((
      New-Object DirectoryServices.ActiveDirectoryAccessRule(
        $identity,
        [DirectoryServices.ActiveDirectoryRights] "Self",
        [Security.AccessControl.AccessControlType] "Allow",
        [Guid] $AD_RIGHTS_GUID_VALIDATED_WRITE_DNS
      )
    ))
    #--------------------------------------------------------------------------
    # Validated write to service principal name
    #--------------------------------------------------------------------------
    [Void] $accessControlEntries.Add((
      New-Object DirectoryServices.ActiveDirectoryAccessRule(
        $identity,
        [DirectoryServices.ActiveDirectoryRights] "Self",
        [Security.AccessControl.AccessControlType] "Allow",
        [Guid] $AD_RIGHTS_GUID_VALIDATED_WRITE_SPN
      )
    ))
    #--------------------------------------------------------------------------
    # Write account restrictions
    #--------------------------------------------------------------------------
    [Void] $accessControlEntries.Add((
      New-Object DirectoryServices.ActiveDirectoryAccessRule(
        $identity,
        [DirectoryServices.ActiveDirectoryRights] "WriteProperty",
        [Security.AccessControl.AccessControlType] "Allow",
        [Guid] $AD_RIGHTS_GUID_ACCT_RESTRICTIONS
      )
    ))
    # Get ActiveDirectorySecurity object
    $adSecurity = $dirEntry.ObjectSecurity
    # Add ACEs to ActiveDirectorySecurity object
    $accessControlEntries | ForEach-Object {
      $adSecurity.AddAccessRule($_)
    }
    # Commit changes
    try {
      $dirEntry.CommitChanges()
    }
    catch [Management.Automation.MethodInvocationException] {
      Write-Error -Exception $_.Exception.InnerException
    }
  }
}
process {
  foreach ( $nameItem in $Name ) {
    Grant-ComputerJoinPermission $nameItem
  }
}