Добавьте сценарий Get-ADPathname.ps1 в свой арсенал инструментов
Всем администраторам Active Directory (AD) приходится иметь дело с путями к объектам в AD. Обычно эти пути представляют собой различающиеся имена (DN) объектов в каталоге (например, «CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com»). По разным причинам фрагменты этих имен иногда требуется извлекать. Например, синтаксический разбор строк часто выполняют для того, чтобы получить родительский контейнер именованного объекта AD. Один из способов это сделать — найти первую запятую в DN и извлечь остаток строки после запятой, как показано на экране 1.
Экран 1. Анализ строки с целью получения родительского контейнера именованного объекта AD |
Однако у простого разбора строки есть свои ограничения. В частности, запятая не является незаконным символом в DN, но ее необходимо экранировать с помощью символа обратной косой черты. То есть если имя объекта «Dyer, Ken» вместо «Ken Dyer», то DN — «CN=Dyer, Ken,CN=Users,DC=fabrikam,DC=com». Результатом разбора строки по методу, показанному на экране 1, будет «Ken,CN=Users,DC=fabrikam,DC=com» (с ведущим пробелом) из-за запятой в первом элементе DN («CN=Dyer, Ken»).
Запятые — не единственные символы, нуждающиеся в экранировании. Также необходимо экранировать косую черту (/) при задании DN для использования с Active Directory Service Interfaces (ADSI). Например, рассмотрим команду:
$user = [ADSI] «LDAP://CN=HRUser,OU=H/R,DC=fabrikam,DC=com»
Попытка выполнить эту команду завершится неудачей, так как косая черта в имени организационной единицы (OU) («CN=Jeff Smith,OU=H/R,DC=fabrikam,DC=com») не экранирована. Чтобы команда стала работоспособной, необходимо экранировать косую черту обратной косой чертой:
$user = [ADSI] «LDAP://CN=HRUser,OU=H/R,DC=fabrikam,DC=com»
Если ни одно из имен объектов не содержит запятых, символов косой черты или других символов, требующих экранирования, синтаксический разбор строк принесет желаемые результаты (пока в AD не встретится объект, содержащий неожиданный символ). Однако эффективность сценариев не должна зависеть от конкретных соглашений об именовании в AD. Сценарии должны выполняться со всеми допустимыми именами AD, независимо от требования экранирования символов.
Надлежащим образом экранировать пути AD сложнее, чем кажется на первый взгляд, поэтому компания Microsoft предоставила COM-объект Pathname. Применить объект Pathname в сценарии VBScript довольно просто, но использовать его в Windows PowerShell не столь удобно из-за отсутствия библиотеки типов. У COM-объекта NameTranslate — такая же проблема с типами. Для взаимодействия с объектом Pathname необходимо использовать программный код, аналогичный коду для объекта NameTranslate. В частности, нужно вызвать метод InvokeMember базового объекта Pathname.
Знакомство с Get-ADPathname.ps1
Вместо того чтобы постоянно иметь дело с неуклюжим синтаксисом метода InvokeMember, я подготовил сценарий PowerShell, Get-ADPathname.ps1, который предоставляет простой способ взаимодействия с объектом Pathname. Сценарий может выполнять семь типов действий на основе указанных пользователем параметров. Эти параметры действий перечислены в таблице 1. Всем параметрам действий в таблице 1 (кроме -GetEscapedElement) также требуется параметр –Path, указывающий пути AD, которые нужно разобрать.
Помимо параметров действия, перечисленных в таблице 1, существует набор параметров-модификаторов, влияющих на входные (-Path и -Type) и выходные (-Format, -EscapedMode и -ValuesOnly) данные сценария. Эти параметры перечислены в таблице 2. Каждый параметр действия в таблице 1 (кроме -GetEscapedElement) использует один или несколько параметров-модификаторов из таблицы 2.
Параметр –Format, приведенный в таблице 2, имеет девять возможных значений, указывающих Get-ADPathname.ps1, как выводить пути AD. Эти значения перечислены в таблице 3.
Теперь, после знакомства с параметрами и доступными значениями, я покажу, как следует их использовать. Лучший способ это сделать — продемонстрировать способы применения каждого из семи параметров в таблице 1. Сценарий Get-ADPathname.ps1 приведен в листинге 1.
Использование параметра -Retrieve
Параметр -Retrieve извлекает пути AD в различных форматах. Это параметр по умолчанию. Его синтаксис следующий:
Get-ADPathname.ps1 [-Path ] [-Type ] [-Retrieve] [-Format ] [-EscapedMode ] [-ValuesOnly]
Имя параметра -Retrieve вводить не обязательно, но его можно указать для ясности. Имя параметра -Path также указывать не обязательно. В приведенных в статье командах я иногда ввожу имя параметра -Path, а в других случаях пропускаю, чтобы читатель привык к обоим способам употребления. Помимо строки, параметр -Path также принимает входные данные конвейера.
Получить пути AD полезно в нескольких случаях. Например, если имеется список имен DN, каждое из которых нужно передать в акселератор типа [ADSI]. Если имена DN находятся в файле с именем DNs.txt, можно использовать следующую команду:
Get-Content DNs.txt | Get-ADPathname -Type DN -Retrieve ` -Format X500 -EscapedMode On | ForEach-Object { ([ADSI] $_).displayName) }
Акселератор типа [ADSI] требует полного пути LDAP для каждого имени DN в списке (-Format X500) и соответствующего экранирования (-EscapedMode On). В результате работы команды выводится атрибут displayName для каждого DN в файле DNs.txt. Обратите внимание, что можно было бы пропустить omitted -Type DN, -Retrieve и -Format X500, так как все эти параметры имеют значения по умолчанию, но указаны для ясности.
Другой пример: имеется список полных путей LDAP (то есть LDAP://...) с escape-символами в файле с именем FullPaths.txt. Из этого списка путей LDAP нужно создать список имен DN без escape-символов. Сделать это можно с помощью следующей команды:
Get-Content FullPaths.txt | Get-ADPathname -Type Full -Retrieve -Format X500DN ` -EscapedMode Off
Аргумент параметра -Type (Full) указывает сценарию Get-ADPathname.ps1, что каждый входной путь (то есть каждая строка в файле FullPaths.txt) представляет собой полный путь LDAP. В этом случае параметры -Format и -EscapedMode не обязательны, так как -Format X500DN и -EscapedMode Off действуют по умолчанию (указаны для ясности).
И, наконец, пример команды, которая получает имя объекта:
Get-ADPathname «CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com» ` -Type DN -Retrieve -Format Leaf -ValuesOnly
Эта команда выводит строку «Ken Dyer» (без кавычек). Если в данной команде пропустить параметр -ValuesOnly, то будет выдана строка «CN=Ken Dyer» (без кавычек).
Использование параметра -AddLeafElement
Параметр –AddLeafElement добавляет конечные элементы к пути AD. Синтаксис следующий:
Get-ADPathname.ps1 [-Path] [-Type ] -AddLeafElement [-Format ] [-EscapedMode ] [-ValuesOnly]
Например, добавляем к пути AD два конечных элемента с использованием -AddLeafElement:
Get-ADPathname «CN=Users,DC=fabrikam,DC=com» ` -AddLeafElement «CN=Ken Dyer»,«CN=Jeff Smith»
На экране 2 показаны результаты. В большинстве случаев параметр -AddLeafElement не столь полезен, поскольку тех же целей можно достичь путем объединения строк в PowerShell, но я указал его для полноты.
Экран 2. Использование параметра -AddLeafElement |
Использование параметра -RemoveLeafElement
Параметр -RemoveLeafElement удаляет конечный элемент из одного или нескольких путей AD, то есть выдает родительские пути именованных объектов AD. Синтаксис следующий:
Get-ADPathname.ps1 [-Path] [-Type ] -RemoveLeafElement [-Format ] [-EscapedMode ] [-ValuesOnly]
Ниже приводится пример использования параметра -RemoveLeafElement для вывода имен родительского контейнера двух путей AD с включенным экранированием:
Get-ADPathname «CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com», «CN=Jeff Smith,OU=H/R,DC=fabrikam,DC=com» ` -RemoveLeafElement -EscapedMode On
Результаты показаны на экране 3. Обратите внимание на вторую OU в выводе.
Экран 3. Использование параметра -RemoveLeafElement |
Использование параметра -Split
Параметр -Split разбивает путь AD и выводит отдельные элементы пути в виде списка. Синтаксис следующий:
Get-ADPathname.ps1 [-Path] –Split [-EscapedMode ] [-ValuesOnly]
Пример параметра -Split в действии:
$userDN = «CN=Jeff Smith,OU=H/R,DC=fabrikam,DC=com» $pathElements = Get-ADPathname $userDN -Split –ValuesOnly «Object name: $($pathElements[0])» «Container name: $($pathElements[1])»
В этом фрагменте исходного текста в параметре -ValuesOnly пропущены CN= и OU= фрагменты имени. Результаты показаны на экране 4.
Экран 4. Использование параметра -Split |
Использование параметра -GetEscapedElement
Параметр -GetEscapedElement выводит элемент пути AD с escape-символами. Этот параметр полезен, так как позволяет не искать символы, которые следует экранировать. Синтаксис следующий:
Get-ADPathname.ps1 -GetEscapedElement
Рассмотрим команду:
Get-ADPathname -GetEscapedElement «CN=Dyer, Ken»
Эта команда выводит строку «CN=Dyer, Ken» (без кавычек).
Использование параметра -GetElement
Параметр -GetElement выводит определенный элемент из одного или нескольких путей AD. Синтаксис следующий:
Get-ADPathname.ps1 [-Path] [-Type ] [-EscapedMode ] [-ValuesOnly]
Крайний левый элемент пути AD имеет номер 0, следующий имеет номер 1 и т.д. Этот параметр полезен, если нужно получить определенный элемент пути. Например, рассмотрим команду
Get-ADPathname -Path ` «CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com» -GetElement 1
Эта команда выводит второй элемент пути: «CN=Users» (без кавычек).
Использование параметра -GetNumElements
Параметр -GetNumElements выдает число элементов в одном или нескольких путях AD. Синтаксис следующий:
Get-ADPathname.ps1 [-Path] [-Type ] -GetNumElements
Пример использования параметра -GetNumElements:
Get-ADPathname -Path ` «CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com» -GetNumElements
Эта команда выдает число 4, так как путь AD содержит 4 элемента («CN=Ken Dyer», «CN=Users», «DC=fabrikam» и «DC=com»). Параметр не обязательный, так как параметр -Split выдает число элементов, но он указывается для полноты.
Хватит разбирать пути AD вручную
.
Листинг 1. Сценарий Get-ADPathname.ps1
# Get-ADPathname.ps1 # Written by Bill Stewart (bstewart@iname.com) # PowerShell wrapper script for the Pathname COM object. #requires -version 2 [CmdletBinding(DefaultParameterSetName=«Retrieve»)] param( [parameter(ParameterSetName=«Retrieve»,Position=0,ValueFromPipeline=$TRUE)] [parameter(ParameterSetName=«AddLeafElement»,Position=0,Mandatory=$TRUE)] [parameter(ParameterSetName=«RemoveLeafElement»,Position=0,Mandatory=$TRUE)] [parameter(ParameterSetName=«GetElement»,Position=0,Mandatory=$TRUE)] [parameter(ParameterSetName=«GetNumElements»,Position=0,Mandatory=$TRUE)] [parameter(ParameterSetName=«Split»,Position=0,Mandatory=$TRUE)] [String[]] $Path, [parameter(ParameterSetName=«Retrieve»)] [parameter(ParameterSetName=«AddLeafElement»)] [parameter(ParameterSetName=«RemoveLeafElement»)] [parameter(ParameterSetName=«GetElement»)] [parameter(ParameterSetName=«GetNumElements»)] [parameter(ParameterSetName=«Split»)] [String] [ValidateSet(«DN",»Full«)] $Type, [parameter(ParameterSetName=»Retrieve«)] [Switch] $Retrieve, [parameter(ParameterSetName=»AddLeafElement«,Mandatory=$TRUE)] [String[]] $AddLeafElement, [parameter(ParameterSetName=»GetElement«,Mandatory=$TRUE)] [UInt32] $GetElement, [parameter(ParameterSetName=»RemoveLeafElement«,Mandatory=$TRUE)] [Switch] $RemoveLeafElement, [parameter(ParameterSetName=»GetNumElements«,Mandatory=$TRUE)] [Switch] $GetNumElements, [parameter(ParameterSetName=»Split«,Mandatory=$TRUE)] [Switch] $Split, [parameter(ParameterSetName=»Retrieve«)] [parameter(ParameterSetName=»AddLeafElement«)] [parameter(ParameterSetName=»RemoveLeafElement«)] [String] [ValidateSet(»Windows«,"WindowsNoServer»,«WindowsDN»,«WindowsParent»,«X500»,«X500NoServer»,«X500DN»,«X500Parent»,«Server»,«Provider»,«Leaf»)] $Format, [parameter(ParameterSetName=«Retrieve»)] [parameter(ParameterSetName=«AddLeafElement»)] [parameter(ParameterSetName=«RemoveLeafElement»)] [parameter(ParameterSetName=«GetElement»)] [parameter(ParameterSetName=«Split»)] [String] [ValidateSet(«Default»,«On",»Off«,"OffEx»)] $EscapedMode, [parameter(ParameterSetName=«Retrieve»)] [parameter(ParameterSetName=«AddLeafElement»)] [parameter(ParameterSetName=«RemoveLeafElement»)] [parameter(ParameterSetName=«GetElement»)] [parameter(ParameterSetName=«Split»)] [Switch] $ValuesOnly, [parameter(ParameterSetName=«GetEscapedElement»,Mandatory=$TRUE)] [String[]] $GetEscapedElement ) begin { $ParamSetName = $PSCMDLET.ParameterSetName # Determine if we're using pipeline input. $PipelineInput = $FALSE if ( $ParamSetName -eq «Retrieve» ) { $PipelineInput = -not $PSBoundParameters.ContainsKey(«Path») } # These hash tables improve code readability. $InputTypes = @{ «Full» = 1 «DN" = 4 } $OutputFormats = @{ »Windows«= 1 »WindowsNoServer«= 2 »WindowsDN«= 3 »WindowsParent«= 4 »X500«= 5 »X500NoServer«= 6 »X500DN«= 7 »X500Parent«= 8 »Server«= 9 »Provider«= 10 »Leaf«= 11 } $EscapedModes = @{ »Default«= 1 »On«= 2 »Off«= 3 »OffEx«= 4 } $DisplayTypes = @{ »Full«= 1 »ValuesOnly«= 2 } # Invokes a method on a COM object that lacks a type library. If the COM # object uses more than one parameter, specify an array as the $parameters # parameter. The $outputType parameter coerces the function's output to the # specified type (default is [String]). function Invoke-Method { param( [__ComObject] $object, [String] $method, $parameters, [System.Type] $outputType =»String« ) $output = $object.GetType().InvokeMember($method,»InvokeMethod«, $NULL, $object, $parameters) if ( $output ) { $output -as $outputType } } # Sets a property on a COM object that lacks a type library. function Set-Property { param( [__ComObject] $object, [String] $property, $parameters ) [Void] $object.GetType().InvokeMember($property,»SetProperty«, $NULL, $object, $parameters) } # Creates the Pathname COM object. It lacks a type library so we use the # above Invoke-Method and Set-Property functions to interact with it. $Pathname = new-object -comobject»Pathname« # Set defaults for -Type and -Format. Use separate variables in case of # pipeline input. if ( $Type ) { $InputType = $Type } else { $InputType =»DN«} if ( $Format ) { $OutputFormat = $Format } else { $OutputFormat =»X500DN«} # Enable escaped mode if requested. if ( $EscapedMode ) { Set-Property $Pathname»EscapedMode«$EscapedModes[$EscapedMode] } # Output values only if requested. if ( $ValuesOnly ) { Invoke-Method $Pathname»SetDisplayType«$DisplayTypes[»ValuesOnly«] } # -Retrieve parameter function Get-ADPathname-Retrieve { param( [String] $path, [Int] $inputType, [Int] $outputFormat ) try { Invoke-Method $Pathname»Set«($path,$inputType) Invoke-Method $Pathname»Retrieve«$outputFormat } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -AddLeafElement parameter function Get-ADPathname-AddLeafElement { param( [String] $path, [Int] $inputType, [String] $element, [Int] $outputFormat ) try { Invoke-Method $Pathname»Set«($path,$inputType) Invoke-Method $Pathname»AddLeafElement«$element Invoke-Method $Pathname»Retrieve«$outputFormat } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -RemoveLeafElement parameter function Get-ADPathname-RemoveLeafElement { param( [String] $path, [Int] $inputType, [Int] $outputFormat ) try { Invoke-Method $Pathname»Set«($path,$inputType) Invoke-Method $Pathname»RemoveLeafElement« Invoke-Method $Pathname»Retrieve«$outputFormat } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -GetElement parameter function Get-ADPathname-GetElement { param( [String] $path, [Int] $inputType, [Int] $elementIndex ) try { Invoke-Method $Pathname»Set«($path,$inputType) Invoke-Method $Pathname»GetElement«$elementIndex } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -GetNumElements parameter function Get-ADPathname-GetNumElements { param( [String] $path, [Int] $inputType ) try { Invoke-Method $Pathname»Set«($path,$inputType) Invoke-Method $Pathname»GetNumElements«-outputtype»UInt32« } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -Split parameter function Get-ADPathname-Split { param( [String] $path, [Int] $inputType ) try { Invoke-Method $Pathname»Set«($path,$inputType) $numElements = Invoke-Method $Pathname»GetNumElements«-outputtype»UInt32« for ( $i = 0; $i -lt $numElements; $i++ ) { Invoke-Method $Pathname»GetElement«$i } } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } # -GetEscapedElement parameter function Get-ADPathname-GetEscapedElement { param( [String] $element ) try { Invoke-Method $Pathname»GetEscapedElement«(0,$element) } catch [System.Management.Automation.MethodInvocationException] { write-error -exception $_.Exception.InnerException } } } process { # The process block uses 'if'/'elseif' instead of 'switch' because 'switch' # replaces '$_', and we need '$_' in case of pipeline input. #»Retrieve«is the only parameter set that that accepts pipeline input. if ( $ParamSetName -eq»Retrieve«) { if ( $PipelineInput ) { if ( $_ ) { Get-ADPathname-Retrieve $_ $InputTypes[$InputType] $OutputFormats[$OutputFormat] } else { write-error»You must provide pipeline input or specify the -Path parameter.«-category SyntaxError } } else { $Path | foreach-object { Get-ADPathname-Retrieve $_ $InputTypes[$InputType] $OutputFormats[$OutputFormat] } } } elseif ( $ParamSetName -eq»AddLeafElement«) { $AddLeafElement | foreach-object { Get-ADPathname-AddLeafElement $Path[0] $InputTypes[$InputType] $_ $OutputFormats[$OutputFormat] } } elseif ( $ParamSetName -eq»RemoveLeafElement«) { $Path | foreach-object { Get-ADPathname-RemoveLeafElement $_ $InputTypes[$InputType] $OutputFormats[$OutputFormat] } } elseif ( $ParamSetName -eq»GetElement«) { $Path | foreach-object { Get-ADPathname-GetElement $_ $InputTypes[$InputType] $GetElement } } elseif ( $ParamSetName -eq»GetNumElements«) { $Path | foreach-object { Get-ADPathname-GetNumElements $_ $InputTypes[$InputType] } } elseif ( $ParamSetName -eq»Split«) { Get-ADPathname-Split $Path[0] $InputTypes[$InputType] } elseif ( $ParamSetName -eq»GetEscapedElement" ) { $GetEscapedElement | foreach-object { Get-ADPathname-GetEscapedElement $_ } } }