Skip to content

Instantly share code, notes, and snippets.

@santisq
Last active November 29, 2022 21:59
Show Gist options
  • Save santisq/1af2e7e78c625838efaa06bd83e250a5 to your computer and use it in GitHub Desktop.
Save santisq/1af2e7e78c625838efaa06bd83e250a5 to your computer and use it in GitHub Desktop.
tree function for AD OUs and Containers
Hierarchy              ObjectClass
---------              -----------
myDomain               domainDNS
├── Workstations       organizationalUnit
├── SomeOU             organizationalUnit
│   ├── OtherOU1       organizationalUnit
│   └── OtherOU2       organizationalUnit
├── TestOU             organizationalUnit
├── People             organizationalUnit
├── Operations         organizationalUnit
└── Domain Controllers organizationalUnit
using namespace System.Collections.Generic
using namespace Microsoft.ActiveDirectory.Management
using namespace System.Management.Automation
function Get-TreeOrganizationalUnit {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string] $Identity,
[Parameter()]
[switch] $IncludeContainer
)
end {
$filter = "(|(name=$Identity)(samAccountName=$Identity)(distinguishedName=$Identity))"
$object = Get-ADObject -LDAPFilter $filter
if(-not $object) {
return $PSCmdlet.WriteWarning("Cannot find an object with Identity: '$Identity'.")
}
if($object.Count -gt 1) {
$errorRecord = [ErrorRecord]::new(
[Exception] "More than one object found with Identity: '$Identity'. Please use 'DistinguishedName' attribute.",
[string] "AmbiguousResult",
[ErrorCategory]::NotImplemented,
$Identity
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
class Tree {
[string] $Hierarchy
[string] $ObjectClass
hidden [int] $Depth
hidden [string] $Base
Tree([ADObject] $Object, [int] $Depth) {
$this.Hierarchy = [Tree]::Indent($Object.Name, $Depth)
$this.ObjectClass = $Object.ObjectClass
$this.Base = $Object.DistinguishedName
$this.Depth = $Depth
}
static [string] Indent([string] $String, [Int64] $Indentation) {
return "$(' ' * $Indentation)$String"
}
static [void] DrawTree([object[]] $InputObject, [PSCmdlet] $Cmdlet) {
$corner, $horizontal, $pipe, $connector = '└', '─', '│', '├'
$cornerConnector = "${corner}$(${horizontal}*2) "
foreach($group in $InputObject | Group-Object Depth | Select-Object -Skip 1) {
foreach($item in $group.Group) {
$item.Hierarchy = $item.Hierarchy -replace '\s{4}(?=\S)', $cornerConnector
}
}
for($i = 1; $i -lt $InputObject.Count; $i++) {
$index = $InputObject[$i].Hierarchy.IndexOf($corner)
if($index -ge 0) {
$z = $i - 1
while($InputObject[$z].Hierarchy[$index] -notmatch "$corner|\S") {
$replace = $InputObject[$z].Hierarchy.ToCharArray()
$replace[$Index] = $pipe
$InputObject[$z].Hierarchy = [string]::new($replace)
$z--
}
if($InputObject[$z].Hierarchy[$index] -eq $corner) {
$replace = $InputObject[$z].Hierarchy.ToCharArray()
$replace[$Index] = $connector
$InputObject[$z].Hierarchy = [string]::new($replace)
}
}
}
$Cmdlet.WriteObject($InputObject, $true)
}
}
$stack = [Stack[Tree]]::new()
$stack.Push([Tree]::new($object, 0))
$param = @{
LDAPFilter = '(objectClass=organizationalUnit)'
SearchScope = 'OneLevel'
}
if($IncludeContainer.IsPresent) {
$param['LDAPFilter'] = '(|(objectClass=container)' + $param['LDAPFilter'] + ')'
}
[Tree]::DrawTree(@(
while($stack.Count) {
$target = $stack.Pop()
$target
$param['SearchBase'] = $target.Base
foreach($object in Get-ADObject @param) {
$stack.Push([Tree]::new($object, $target.Depth + 1))
}
}
), $PSCmdlet)
}
}
@mattcargile
Copy link

mattcargile commented Nov 29, 2022

I just couldn't get the filter working on this. I think it maybe the extra parens?

https://gist.github.com/santisq/1af2e7e78c625838efaa06bd83e250a5#file-get-treeorganizationalunint-ps1-L16

P.S. Unit is mispelled. :-)

@santisq
Copy link
Author

santisq commented Nov 29, 2022

I just couldn't get the filter working on this. I think it maybe the extra parens?

https://gist.github.com/santisq/1af2e7e78c625838efaa06bd83e250a5#file-get-treeorganizationalunint-ps1-L16

P.S. Unit is mispelled. :-)

@mattcargile thanks Matt, didn't even notice the typo. Also messed up with the LDAP Filter while editing it. Thanks for letting me know, should be good now 👍

@mattcargile
Copy link

Works now! 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment