ADName.psm1
# ADName.psm1 # Written by Bill Stewart (bstewart@iname.com) # # Implements the NameTranslate and Pathname COM objects as easy-to-use # PowerShell functions. # # Version history: # # 1.0.0.0 (2017-04-06) # * Initial version. Based on my articles Translate-ADName.ps1 and # Get-ADPathname.ps1 that I published in Windows IT Pro. I refactored and # cleaned up the code and made it (I hope) easier to read. # # 1.0.0.2 (2017-07-25) # * Renamed Edit-ADName to Get-ADName. #requires -version 3 #------------------------------------------------------------------------------ # IADsNameTranslate enumeration values #------------------------------------------------------------------------------ $ADS_NAME_INITTYPE_ENUM = @{ "Domain" = 1 "Server" = 2 "GC" = 3 } $ADS_NAME_TYPE_ENUM = @{ "1779" = 1 "DN" = 1 "X500DN" = 1 "Canonical" = 2 "NT4" = 3 "Display" = 4 "DomainSimple" = 5 "EnterpriseSimple" = 6 "GUID" = 7 "Unknown" = 8 "UPN" = 9 "CanonicalEx" = 10 "SPN" = 11 "SIDorSIDhistory" = 12 } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # IADsPathname enumeration values #------------------------------------------------------------------------------ $ADS_SETTYPE_ENUM = @{ "Full" = 1 "DN" = 4 } $ADS_FORMAT_ENUM = @{ "Windows" = 1 "WindowsNoServer" = 2 "WindowsDN" = 3 "WindowsParent" = 4 "X500" = 5 "X500NoServer" = 6 "X500DN" = 7 "DN" = 7 "1779" = 7 "X500Parent" = 8 "Parent" = 8 "Server" = 9 "Provider" = 10 "Leaf" = 11 } $ADS_ESCAPE_MODE_ENUM = @{ "Default" = 1 "On" = 2 "Off" = 3 "OffEx" = 4 } $ADS_DISPLAY_ENUM = @{ "Full" = 1 "ValuesOnly" = 2 } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Wrapper methods for the COM objects #------------------------------------------------------------------------------ function Invoke-Method { param( [__ComObject] $object, [String] $method, $parameters ) $output = $object.GetType().InvokeMember($method, "InvokeMethod", $null, $object, $parameters) if ( $output ) { $output } } function Set-Property { param( [__ComObject] $object, [String] $property, $parameters ) [Void] $object.GetType().InvokeMember($property, "SetProperty", $null, $object, $parameters) } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Create the COM objects #------------------------------------------------------------------------------ $NameTranslate = New-Object -ComObject "NameTranslate" $Pathname = New-Object -ComObject "Pathname" #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Writes a custom error to the error stream #------------------------------------------------------------------------------ function Write-CustomError { param( [Exception] $exception, $targetObject, [String] $errorID, [Management.Automation.ErrorCategory] $errorCategory="NotSpecified", [Switch] $terminatingError ) $errorRecord = New-Object Management.Automation.ErrorRecord($exception,$errorID,$errorCategory,$targetObject) if ( $terminatingError ) { $PSCmdlet.ThrowTerminatingError($errorRecord) } else { $PSCmdlet.WriteError($errorRecord) } } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ function Convert-ADName { <# .SYNOPSIS Converts Active Directory names between various formats. .DESCRIPTION Converts Active Directory (AD) names between various formats by using the NameTranslate COM object. The NameTranslate COM object implements the IADsNameTranslate interface (see RELATED LINKS). The NameTranslate object converts AD names by binding to a global catalog or to a specific domain or server name. .PARAMETER OutputType Specifies the output type for the name(s), which must be one of the following: DN Distinguished name; e.g.: CN=Ken Dyer,CN=Users,DC=... 1779 Same as DN X500DN Same as DN Canonical Canonical name; e.g.: fabrikam.com/Users/Ken Dyer NT4 Domain\username; e.g.: fabrikam\kdyer Display Display name DomainSimple Simple domain name format EnterpriseSimple Simple enterprise name format GUID GUID; e.g.: {95ee9fff-3436-11d1-b2b0-d15ae3ac8436} UPN User principal name; e.g.: kdyer@fabrikam.com CanonicalEx Extended canonical name format SPN Service principal name format This parameter's values correspond to the ADS_NAME_TYPE_ENUM enumeration's values (see RELATED LINKS). .PARAMETER Name Specifies one or more AD name(s) to convert. This parameter does not support wildcards. .PARAMETER InputType Specifies the input name type. Possible values are the same as the values supported by the -OutputType parameter, with the following additions: Unknown The system will estimate the name format SIDorSIDhistory SDDL string for the object's SID or one in its SID history The default value for this parameter is "Unknown". This parameter's values correspond to the ADS_NAME_TYPE_ENUM enumeration's values (see RELATED LINKS). .PARAMETER InitType Specifies how to initialize the NameTranslate object. This parameter must be either "Domain" or "Server". If you omit this parameter, the default is to find and use a global catalog. This parameter's values correspond to the ADS_NAME_INITTYPE_DOMAIN and ADS_NAME_INITTYPE_SERVER values of the ADS_NAME_INITTYPE_ENUM enumeration (see RELATED LINKS). .PARAMETER InitName Specifies the name of the domain or server to bind to when the -InitType parameter is set to "Domain" or "Server". .PARAMETER ChaseReferrals Specifies whether to chase referrals. (When a server determines that other servers hold relevant data, in part or as a whole, it may refer the client to another server to obtain the result. Referral chasing is the action taken by a client to contact the referred-to server to continue the directory search.) .PARAMETER Credential Specifies credentials to use when binding to a global catalog, server, or domain. If you omit this parameter, the current credentials are used. .INPUTS System.String .OUTPUTS System.String .EXAMPLE PS C:\> Convert-ADName Canonical "CN=Ken Dyer,OU=Engineers,DC=fabrikam,DC=com" This command outputs the specified DN as a canonical name. .EXAMPLE PS C:\> Convert-ADName -OutputType DN -Name fabrikam\kdyer This command outputs the specified domain\username as a distinguished name. .EXAMPLE PS C:\> Convert-ADName DN fabrikam\kdyer -InitType Server -InitName dc1 This command uses the server dc1 to convert the specified name. .EXAMPLE PS C:\> Convert-ADName Display fabrikam\kdyer -InitType Domain -InitName fabrikam This command uses the fabrikam domain to convert the specified name. .EXAMPLE PS C:\> Convert-ADName DN "fabrikam.com/Engineers/Ken Dyer" -Credential (Get-Credential) Prompts for credentials, then uses those credentials to convert the specified name. .EXAMPLE PS C:\> Get-Content DNs.txt | Convert-ADName -OutputType Display -InputType DN Outputs the display names for each of the distinguished names in the file DNs.txt. .LINK IADsNameTranslate Interface - http://msdn.microsoft.com/en-us/windows/aa706046.aspx ADS_NAME_TYPE_ENUM Enumeration - http://msdn.microsoft.com/en-us/windows/aa772267.aspx ADS_NAME_INITTYPE_ENUM Enumeration - http://msdn.microsoft.com/en-us/windows/aa772266.aspx #> [CmdletBinding(DefaultParameterSetName="GC")] param( [Parameter(ParameterSetName="GC", Position=0,Mandatory=$true)] [Parameter(ParameterSetName="DomainOrServer",Position=0,Mandatory=$true)] [String] [ValidateSet("DN","1779","X500DN","Canonical","NT4","Display","DomainSimple","EnterpriseSimple","GUID","UPN","CanonicalEx","SPN")] $OutputType, [Parameter(ParameterSetName="GC", Position=1,Mandatory=$true,ValueFromPipeline=$true)] [Parameter(ParameterSetName="DomainOrServer",Position=1,Mandatory=$true,ValueFromPipeline=$true)] [String[]] $Name, [Parameter(ParameterSetName="GC")] [Parameter(ParameterSetName="DomainOrServer")] [String] [ValidateSet("1779","DN","X500DN","Canonical","NT4","Display","DomainSimple","EnterpriseSimple","GUID","Unknown","UPN","CanonicalEx","SPN","SIDorSIDhistory")] $InputType="Unknown", [Parameter(ParameterSetName="DomainOrServer",Mandatory=$true)] [String] [ValidateSet("Domain","Server")] $InitType, [Parameter(ParameterSetName="DomainOrServer",Mandatory=$true)] [String] $InitName, [Parameter(ParameterSetName="GC")] [Parameter(ParameterSetName="DomainOrServer")] [Switch] $ChaseReferrals, [Parameter(ParameterSetName="GC")] [Parameter(ParameterSetName="DomainOrServer")] [Management.Automation.PSCredential] $Credential ) begin { # Use global catalog by default (use separate variable because $InitType # uses ValidateSet) if ( $PSCmdlet.ParameterSetName -eq "GC" ) { $InitTypeName = "GC" } else { $InitTypeName = $InitType } # If -Credential, use InitEx if ( $Credential ) { $networkCredential = $Credential.GetNetworkCredential() try { Invoke-Method $NameTranslate "InitEx" ( $ADS_NAME_INITTYPE_ENUM[$InitTypeName], $InitName, $networkCredential.UserName, $networkCredential.Domain, $networkCredential.Password ) } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $null ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified ` -terminatingError } finally { Remove-Variable networkCredential } } else { try { Invoke-Method $NameTranslate "Init" ( $ADS_NAME_INITTYPE_ENUM[$InitTypeName], $InitName ) } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $null ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified ` -terminatingError } } # If -ChaseReferrals, set the object's ChaseReferral property to # ADS_CHASE_REFERRALS_ALWAYS (0x60 - only supported value); otherwise use # 0 (ADS_CHASE_REFERRALS_NEVER) if ( $ChaseReferrals ) { Set-Property $NameTranslate "ChaseReferral" @(0x60) } else { Set-Property $NameTranslate "ChaseReferral" @(0) } function NameTranslate { param( [String] $name, [Int] $inputType, [Int] $outputType ) try { Invoke-Method $NameTranslate "Set" @($inputType, $name) Invoke-Method $NameTranslate "Get" @($outputType) } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $name ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } } process { foreach ( $nameItem in $Name ) { NameTranslate $nameItem $ADS_NAME_TYPE_ENUM[$InputType] $ADS_NAME_TYPE_ENUM[$OutputType] } } } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ function Get-ADName { <# .SYNOPSIS Outputs Active Directory path names in various formats. .DESCRIPTION Outputs Active Directory (AD) path names in various formats using the Pathname COM object. The Pathname COM object implements the IADsPathname interface (see RELATED LINKS). This is a more robust means of handling AD path names than string parsing because it supports escaping of special characters. .PARAMETER Name Specifies one or more AD path names, in distinguished name (DN) format; e.g.: "CN=Ken Dyer,DC=fabrikam,DC=com". If using the Full type (see -Type parameter), include the provider and/or server; e.g.: "LDAP://CN=Ken Dyer,DC=fabrikam,DC=com" or "LDAP://server/CN=Ken Dyer,DC=fabrikam,DC=com". This parameter does not accept wildcards. .PARAMETER Format Specifies the format in which to output the AD path name(s), and must be one of the following values: DN Distinguished name (DN) format X500DN Same as DN 1779 Same as DN X500 DN format with provider X500NoServer DN format with provider but without server Parent DN format, parent only Leaf Leaf only Provider Provider only (e.g., "LDAP") X500Parent Same as Parent Server Server name only Windows Windows format (rarely used) WindowsNoServer Windows format without server (rarely used) WindowsDN Windows format, distinguished name only (rarely used) WindowsParent Windows format, parent only (rarely used) The default value for this parameter is "DN". This parameter's values correspond to the ADS_FORMAT_ENUM enumeration's values (see RELATED LINKS). .PARAMETER Type Specifies the type of the AD path name(s). This parameter must be one of the following values: "DN" or "Full". If you specify "Full", include the provider and/or server. .PARAMETER Retrieve Outputs the AD path name(s) using the format specified by the -Format parameter. This is the default parameter. .PARAMETER AddLeafElement Adds the specified leaf element(s) to the AD path name(s) and outputs the new AD path name(s) using the format specified by the -Format parameter. .PARAMETER RemoveLeafElement Removes the final leaf element from the AD path name(s) and outputs the new AD path name(s) using the format specified by the -Format parameter. .PARAMETER Split Outputs a list of the elements in the AD path name(s). .PARAMETER GetEscapedElement Outputs one or more AD path name element(s) with escape ("\") characters inserted in the correct places. .PARAMETER EscapedMode Specifies how escape characters are output for the AD path name(s). This parameter must be one of the following values: "Default", "On", "Off", or "OffEx". The default value for this parameter is "Default". This parameter's values correspond to the ADS_ESCAPE_MODE_ENUM enumeration's values (see RELATED LINKS). .PARAMETER ValuesOnly Specifies how elements in an AD path name are output. If this parameter is absent, name elements are output using both attributes and values (e.g., "CN=Ken Dyer"). If this parameter is present, name elements are output with values only (e.g., "Ken Dyer"). .INPUTS System.String .OUTPUTS System.String .EXAMPLE PS C:\> Get-ADName "LDAP://CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com" -Type Full Outputs "CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com". The -Type parameter indicates that the AD path name contains a provider (LDAP). .EXAMPLE PS C:\> Get-ADName "CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com" -RemoveLeafElement This command removes the leaf element ("CN=Ken Dyer") from the AD path name and outputs "CN=Users,DC=fabrikam,DC=com". .EXAMPLE PS C:\> Get-ADName "CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com" -Format Parent This command outputs only the parent of the AD path name; e.g.: "CN=Users,DC=fabrikam,DC=com". .EXAMPLE PS C:\> Get-ADName "CN=Jeff Smith,CN=H/R,DC=fabrikam,DC=com" -Format X500 This commands outputs the AD path element in X500 format "LDAP://CN=Jeff Smith,CN=H\/R,DC=fabrikam,DC=com". Note that the X500 format automatically includes escape characters (i.e., it is not necessary to use -EscapedMode On). .EXAMPLE PS C:\> Get-ADName "CN=H/R,DC=fabrikam,DC=com" -AddLeafElement "CN=Jeff Smith" -EscapedMode On This command outputs "CN=Jeff Smith,CN=H\/R,DC=fabrikam,DC=com" with necessary escape characters inserted. .EXAMPLE PS C:\> Get-ADName "CN=Ken Dyer,CN=Users,DC=fabrikam,DC=com" -Split This command splits the AD path name and outputs a list of the elements: "CN=Ken Dyer", "CN=Users", "DC=fabrikam", and "DC=com". .EXAMPLE PS C:\> Get-Content DistinguishedNames.txt | Get-ADName -EscapedMode On This command outputs all of the AD path names listed in the file DistinguishedNames.txt with the needed escape characters. .EXAMPLE PS C:\> Get-ADName -GetEscapedElement "OU=H/R" This command inserts the needed escape characters into the name element and outputs "OU=H\/R". .LINK IADsPathname Interface - http://msdn.microsoft.com/en-us/library/windows/desktop/aa706070.aspx ADS_FORMAT_ENUM Enumeration - http://msdn.microsoft.com/en-us/library/windows/desktop/aa772261.aspx ADS_ESCAPE_MODE_ENUM Enumeration - http://msdn.microsoft.com/en-us/library/windows/desktop/aa772257.aspx #> [CmdletBinding(DefaultParameterSetName="Retrieve")] param( [Parameter(ParameterSetName="Retrieve", Position=0,Mandatory=$true,ValueFromPipeline=$true)] [Parameter(ParameterSetName="AddLeafElement", Position=0,Mandatory=$true,ValueFromPipeline=$true)] [Parameter(ParameterSetName="RemoveLeafElement",Position=0,Mandatory=$true,ValueFromPipeline=$true)] [Parameter(ParameterSetName="Split", Position=0,Mandatory=$true,ValueFromPipeline=$true)] [Alias("Path")] [String[]] $Name, [Parameter(ParameterSetName="Retrieve", Position=1)] [Parameter(ParameterSetName="AddLeafElement", Position=1)] [Parameter(ParameterSetName="RemoveLeafElement",Position=1)] [Alias("OutputType")] [String] [ValidateSet("DN","X500DN","1779","X500","X500NoServer","Parent","Leaf","Provider","X500Parent","Server","Windows","WindowsNoServer","WindowsDN","WindowsParent")] $Format="DN", [Parameter(ParameterSetName="Retrieve")] [Parameter(ParameterSetName="AddLeafElement")] [Parameter(ParameterSetName="RemoveLeafElement")] [Parameter(ParameterSetName="Split")] [Alias("InputType")] [String] [ValidateSet("DN","Full")] $Type="DN", [Parameter(ParameterSetName="Retrieve")] [Switch] $Retrieve, [Parameter(ParameterSetName="AddLeafElement",Mandatory=$true)] [String[]] $AddLeafElement, [Parameter(ParameterSetName="RemoveLeafElement",Mandatory=$true)] [Switch] $RemoveLeafElement, [Parameter(ParameterSetName="Split",Mandatory=$true)] [Switch] $Split, [Parameter(ParameterSetName="Retrieve")] [Parameter(ParameterSetName="AddLeafElement")] [Parameter(ParameterSetName="RemoveLeafElement")] [Parameter(ParameterSetName="Split")] [String] [ValidateSet("Default","On","Off","OffEx")] $EscapedMode, [Parameter(ParameterSetName="Retrieve")] [Parameter(ParameterSetName="AddLeafElement")] [Parameter(ParameterSetName="RemoveLeafElement")] [Parameter(ParameterSetName="Split")] [Switch] $ValuesOnly, [Parameter(ParameterSetName="GetEscapedElement",Mandatory=$true)] [String[]] $GetEscapedElement ) begin { if ( $EscapedMode ) { Set-Property $Pathname "EscapedMode" $ADS_ESCAPE_MODE_ENUM[$EscapedMode] } else { Set-Property $Pathname "EscapedMode" $ADS_ESCAPE_MODE_ENUM["Default"] } if ( $ValuesOnly ) { Invoke-Method $Pathname "SetDisplayType" $ADS_DISPLAY_ENUM["ValuesOnly"] } else { Invoke-Method $Pathname "SetDisplayType" $ADS_DISPLAY_ENUM["Full"] } function Retrieve { param( [String] $name, [Int] $inputType, [Int] $outputFormat ) try { Invoke-Method $Pathname "Set" @($name,$inputType) Invoke-Method $Pathname "Retrieve" $outputFormat } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $name ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } function AddLeafElement { param( [String] $name, [Int] $inputType, [String] $element, [Int] $outputFormat ) try { Invoke-Method $Pathname "Set" @($name,$inputType) Invoke-Method $Pathname "AddLeafElement" $element Invoke-Method $Pathname "Retrieve" $outputFormat } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $name ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } function RemoveLeafElement { param( [String] $name, [Int] $inputType, [Int] $outputFormat ) try { Invoke-Method $Pathname "Set" @($name,$inputType) Invoke-Method $Pathname "RemoveLeafElement" Invoke-Method $Pathname "Retrieve" $outputFormat } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $name ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } function Split { param( [String] $name, [Int] $inputType ) try { Invoke-Method $Pathname "Set" @($name,$inputType) $numElements = Invoke-Method $Pathname "GetNumElements" for ( $i = 0; $i -lt $numElements; $i++ ) { Invoke-Method $Pathname "GetElement" $i } } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $name ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } function GetEscapedElement { param( [String] $element ) try { Invoke-Method $Pathname "GetEscapedElement" @(0,$element) } catch [Management.Automation.MethodInvocationException] { Write-CustomError ` -exception $_.Exception.InnerException ` -targetObject $element ` -errorID (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name ` -errorCategory NotSpecified } } } process { switch ( $PSCmdlet.ParameterSetName ) { "Retrieve" { foreach ( $nameItem in $Name ) { Retrieve $nameItem $ADS_SETTYPE_ENUM[$Type] $ADS_FORMAT_ENUM[$Format] } } "AddLeafElement" { foreach ( $nameItem in $Name ) { foreach ( $addLeafElementItem in $AddLeafElement ) { AddLeafElement $nameItem $ADS_SETTYPE_ENUM[$Type] $addLeafElementItem $ADS_FORMAT_ENUM[$Format] } } } "RemoveLeafElement" { foreach ( $nameItem in $Name ) { RemoveLeafElement $nameItem $ADS_SETTYPE_ENUM[$Type] $ADS_FORMAT_ENUM[$Format] } } "Split" { foreach ( $nameItem in $Name ) { Split $nameItem $ADS_SETTYPE_ENUM[$Type] } } "GetEscapedElement" { foreach ( $getEscapedElementItem in $GetEscapedElement ) { GetEscapedElement $getEscapedElementItem } } } } } #------------------------------------------------------------------------------ |