Skip to content

Commit e07b172

Browse files
committed
New script
1 parent f2c2f2c commit e07b172

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
function Get-MsDsParentDistinguishedName {
2+
<#
3+
.SYNOPSIS
4+
Get the parent OU of an Active Directory object without depending on the ActiveDirectory module.
5+
6+
.DESCRIPTION
7+
This function returns the DN of the parent organizational unit of an Active Directory object such as a user or computer.
8+
You can look up the object by its sAMAccountName, User Principal Name (UPN), or Distinguished Name (DN). The calculated
9+
attribute 'msDS-ParentDistName' is used to find the parent OU.
10+
11+
This function uses the .NET System.DirectoryServices namespace, which is available cross-platform and allows for LDAP
12+
queries without needing the ActiveDirectory module.
13+
14+
.PARAMETER sAMAccountName
15+
The sAMAccountName of the object to look up.
16+
17+
.PARAMETER UserPrincipalName
18+
The User Principal Name (UPN) of the object to look up.
19+
20+
.PARAMETER DistinguishedName
21+
The Distinguished Name (DN) of the object to look up.
22+
23+
.EXAMPLE
24+
Get-MsDsParentDistinguishedName -sAMAccountName SamErde
25+
26+
This example retrieves the DN of the parent OU for the user with the sAMAccountName "SamErde".
27+
28+
.EXAMPLE
29+
Get-MsDsParentDistinguishedName -userPrincipalName [email protected]
30+
31+
This example retrieves the DN of the parent OU for the user with the UserPrincipalName "[email protected]".
32+
33+
.EXAMPLE
34+
Get-ParentDN -DistinguishedName $((Get-ADUser SamErde).DistinguishedName)
35+
36+
This retrieves the DN of the parent OU for the user with the Distinguished Name "CN=Sam Erde,OU=Users,DC=day3bits,DC=com".
37+
It also uses the shorter alias name for the function.
38+
39+
.NOTES
40+
Some work is still needed on the parameters that accept input from the pipeline. It currently only works with the sAMAccountName parameter.
41+
42+
Starting Points:
43+
44+
# Simple method 1:
45+
$User = ([ADSISearcher]'samAccountName=samerde').FindOne().Properties
46+
$UserCN = [regex]::Escape($User.cn)
47+
$UserOU = ($User.distinguishedname).TrimStart("CN=$UserCN,")
48+
$UserOU
49+
50+
# Slightly less simple method 2:
51+
$UserDN = 'CN=Sam Erde,OU=Users,DC=day3bits,DC=com'
52+
$DirectoryEntry = [ADSI]"LDAP://$UserDN"
53+
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
54+
$Searcher.SearchRoot = $DirectoryEntry
55+
$Searcher.Filter = "(distinguishedName=$UserDN)"
56+
$Searcher.PropertiesToLoad.Add('msDS-ParentDistName') | Out-Null
57+
$Result = $Searcher.FindOne()
58+
$Result
59+
#>
60+
[CmdletBinding(DefaultParameterSetName = 'BysAMAccountName')]
61+
[OutputType([string])]
62+
[Alias('Get-ParentDN')]
63+
64+
param (
65+
66+
<# ⛓️‍💥 I wanted to include this functionality, but relying on the ActiveDirectory namespace defeats the purpose of not using the ActiveDirectory module. ⛓️‍💥
67+
[Parameter(ParameterSetName = 'ByInputObject', Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'A user, computer, or ADObject to look up.')]
68+
[ValidateScript({
69+
if ($_ -is [Microsoft.ActiveDirectory.Management.ADEntity] -or $_ -is [Microsoft.ActiveDirectory.Management.ADAccount] -or $_ -is [Microsoft.ActiveDirectory.Management.ADObject] -or $_ -is [Microsoft.ActiveDirectory.Management.ADComputer] -or $_ -is [Microsoft.ActiveDirectory.Management.ADUser]) {
70+
return $true
71+
} else {
72+
throw "The InputObject parameter requires an ADUser, ADComputer, or ADObject object type as input. A $($_.GetType().Name) was provided."
73+
}
74+
})]
75+
[Microsoft.ActiveDirectory.Management.ADEntity] $InputObject,
76+
#>
77+
78+
[Parameter(ParameterSetName = 'BysAMAccountName', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The sAMAccountName of the object to look up.')]
79+
[string] $sAMAccountName,
80+
81+
[Parameter(ParameterSetName = 'ByUserPrincipalName', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The User Principal Name (UPN) of the object to look up.')]
82+
[string] $UserPrincipalName,
83+
84+
[Parameter(ParameterSetName = 'ByDistinguishedName', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The Distinguished Name (DN) of the object to look up.')]
85+
[string] $DistinguishedName
86+
)
87+
88+
begin {}
89+
90+
process {
91+
# Find the base distinguished name (DN) of the Active Directory forest to search in.
92+
$BaseDN = ([ADSI]'LDAP://RootDSE').defaultNamingContext
93+
$SearchRoot = [ADSI]"LDAP://$BaseDN"
94+
95+
# Define the LDAP filter based on the parameter provided or lookup the DistinguishedName (DN) directly if provided.
96+
# Escape the input parameter values to handle any special characters. DNS are usually already escaped.
97+
switch ($PSCmdlet.ParameterSetName) {
98+
'BysAMAccountName' {
99+
$sAMAccountName = [regex]::Escape($sAMAccountName)
100+
$Filter = "(sAMAccountName=$sAMAccountName)"
101+
}
102+
'ByUserPrincipalName' {
103+
#$UserPrincipalName = [regex]::Escape($UserPrincipalName) # this broke it somehow
104+
$Filter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=$UserPrincipalName))"
105+
}
106+
'ByDistinguishedName' {
107+
$Filter = "(distinguishedName=$DistinguishedName)"
108+
$SearchRoot = [ADSI]"LDAP://$DistinguishedName"
109+
}
110+
}
111+
112+
# Create a DirectorySearcher object to search for the AD object and its msDS-ParentDistName attribute.
113+
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
114+
$Searcher.SearchRoot = $SearchRoot
115+
$Searcher.Filter = $Filter
116+
$Searcher.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
117+
$Searcher.PropertiesToLoad.Add('msDS-ParentDistName') | Out-Null
118+
119+
# Error handling for the DirectorySearcher object's FindOne method.
120+
try {
121+
# Perform the search and retrieve the msDS-ParentDistName attribute.
122+
$Result = $Searcher.FindOne()
123+
124+
# Check if the result is not null and contains the msDS-ParentDistName attribute.
125+
if ($null -ne $Result) {
126+
# Get the msDS-ParentDistName attribute from the result.This contains the DN of the parent OU.
127+
$Parent = $Result.Properties['msds-parentdistname']
128+
if ($Parent) {
129+
# The result is an array of properties, so we need to return the first element.
130+
return $Parent[0]
131+
} else {
132+
Write-Warning 'A result was found while searching for the Active Directory object, but the msDS-ParentDistName attribute was not present. You may not have permission to read the msDS-ParentDistName attribute or are not connected to a global catalog server.'
133+
return $null
134+
}
135+
} else {
136+
# $Result is null, meaning no matching object was found.
137+
Write-Warning 'No matching object was found in Active Directory.'
138+
return $null
139+
}
140+
} catch {
141+
# The search failed, possibly due to an invalid filter or other issues.
142+
Write-Error "An error occurred: $_"
143+
return $null
144+
}
145+
} # End of process block
146+
147+
end {
148+
# Cleanup: Dispose of the DirectorySearcher object to free up resources.
149+
if ($Searcher) {
150+
$Searcher.Dispose()
151+
}
152+
# Remove all variables to free up memory.
153+
Remove-Variable -Name BaseDN, SearchRoot, Filter, Searcher, Result, Parent -ErrorAction SilentlyContinue
154+
}
155+
}

0 commit comments

Comments
 (0)