Created
February 15, 2021 15:06
-
-
Save jdhitsolutions/895d44d68ac4f9523257393d9d4a38d8 to your computer and use it in GitHub Desktop.
A PowerShell script to display an Active Directory domain in colorized and tree form.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# requires -versoin 5.1 | |
# requires -module ActiveDirectory | |
Function Show-DomainTree { | |
[cmdletbinding()] | |
[OutputType("String")] | |
[alias("dt")] | |
Param( | |
[Parameter(Position = 0, HelpMessage = "Specify the domain name. The default is the user domain.")] | |
[ValidateNotNullOrEmpty()] | |
[string]$Name = $env:USERDOMAIN, | |
[Parameter(HelpMessage = "Specify a domain controller to query.")] | |
[alias("dc","domaincontroller")] | |
[string]$Server, | |
[Parameter(HelpMessage = "Specify an alternate credential.")] | |
[alias("RunAs")] | |
[PSCredential]$Credential, | |
[Parameter(HelpMessage = "Display the domain tree using distinguished names.")] | |
[alias("dn")] | |
[switch]$UseDN, | |
[Parameter(HelpMessage = "Include containers and non-OU elements. Items with a GUID in the name will be omitted.")] | |
[alias("cn")] | |
[switch]$Containers | |
) | |
Write-Verbose "Starting $($myinvocation.MyCommand)" | |
Function Get-OUTree { | |
[cmdletbinding()] | |
Param( | |
[string]$Path = (Get-ADDomain).DistinguishedName, | |
[string]$Server, | |
[Parameter(HelpMessage = "Specify an alternate credential")] | |
[PSCredential]$Credential, | |
[Parameter(HelpMessage = "Display the distinguishedname")] | |
[switch]$UseDN, | |
[Parameter(HelpMessage = "Include containers")] | |
[alias("cn")] | |
[switch]$Containers, | |
[Parameter(HelpMessage = "Used in recursion only. You don't need to specify anything")] | |
[Int]$Indent=1, | |
[Parameter(HelpMessage = "Used in recursion only. You don't need to specify anything")] | |
[switch]$Children | |
) | |
Write-Verbose "Searching path $path" | |
function GetIndentString { | |
[CmdletBinding()] | |
Param([int]$Indent) | |
$charHash = @{ | |
upperLeft = [char]0x250c | |
upperRight = [char]0x2510 | |
lowerRight = [char]0x2518 | |
lowerLeft = [char]0x2514 | |
horizontal = [char]0x2500 | |
vertical = [char]0x2502 | |
join = [char]0x251c | |
} | |
if ($Children) { | |
if ($indent -eq 5) { | |
$indent+=2 | |
} | |
elseif ($indent -eq 7) { | |
$indent+=4 | |
} | |
$pad= " "*($Indent) | |
if ($script:IsLast) { | |
#write-Host "LAST" -ForegroundColor magenta | |
$str+=" $pad{0}{1} " -f $charHash.join,([string]$charHash.horizontal * 2 ) | |
} | |
else { | |
$str+="{0}$pad{1}{2} " -f $charHash.vertical,$charHash.join,([string]$charHash.horizontal * 2 ) | |
} | |
} | |
else { | |
if ($script:IsLast) { | |
$c = $charHash.lowerleft | |
} | |
else { | |
$c =$charHash.join | |
} | |
$str = "{0}{1} " -f $c,([string]$charHash.horizontal * 2 ) | |
} | |
$str | |
} | |
#GUID Regex | |
[regex]$Guid = "\w{8}-(\w{4}-){3}\w{12}" | |
#parameters to splat for the search | |
if ($Containers) { | |
$filter = "(|(objectclass=container)(objectclass=organizationalUnit))" | |
} | |
else { | |
$filter = "objectclass=organizationalUnit" | |
} | |
$search = @{ | |
LDAPFilter = $filter | |
SearchScope = "OneLevel" | |
SearchBase = $path | |
Properties = "ProtectedFromAccidentalDeletion" | |
} | |
"Server","Credential" | foreach-Object { | |
if ($PSBoundParameters.ContainsKey($_)) { | |
$search.Add($_,$PSBoundParameters[$_]) | |
} | |
} | |
$data = Get-ADObject @search | Sort-Object -Property DistinguishedName | |
if ($Containers) { | |
#filter out GUID named entries | |
$data = $data | where-object {$_.name -notmatch $GUID} | |
} | |
if ($path -match "^DC\=") { | |
$top = $data | |
$last = $top[-1].distinguishedname | |
$script:IsLast = $False | |
Write-Verbose "Last top level is $last" | |
} | |
if ($data ) { | |
$data | Foreach-Object { | |
if ($UseDN) { | |
$name = $_.distinguishedname | |
} | |
else { | |
$name = $_.name | |
} | |
if ($script:IsLast) { | |
Write-Verbose "Processing last items" | |
} | |
else { | |
$script:IsLast = $_.distinguishedname -eq $last | |
} | |
if ($_.ProtectedFromAccidentalDeletion) { | |
#display protected OUs in color | |
$nameValue = "$([char]0x1b)[38;5;199m$name$([char]0x1b)[0m" | |
} | |
elseif ($_.objectclass -eq 'container') { | |
$nameValue = "$([char]0x1b)[38;5;3m$name$([char]0x1b)[0m" | |
} | |
elseif ($_.objectclass -ne 'organizationalUnit') { | |
#display non-OU and non-Container in a different color | |
$nameValue = "$([char]0x1b)[38;5;211m$name$([char]0x1b)[0m" | |
} | |
else { | |
$nameValue = "$([char]0x1b)[38;5;191m$name$([char]0x1b)[0m" | |
} | |
"{0}{1}" -f (GetIndentString -indent $indent),$nameValue | |
$PSBoundParameters["Path"] = $_.DistinguishedName | |
$PSBoundParameters["Indent"] = $Indent+2 | |
$PSBoundParameters["children"] = $True | |
#call the nested function recursively | |
Get-OUTree @PSBoundParameters | |
} | |
} #if $data | |
} | |
if ($host.name -eq 'ConsoleHost') { | |
$getAD = @{ | |
ErrorAction = "stop" | |
Identity = $Name | |
} | |
"Server","Credential" | foreach-Object { | |
if ($PSBoundParameters.ContainsKey($_)) { | |
$getAD.Add($_,$PSBoundParameters[$_]) | |
} | |
} | |
Try { | |
Write-Verbose "Getting distinguished name for $($Name.toUpper())" | |
$getAD | Out-String | Write-Verbose | |
[string]$Path = (Get-ADDomain @getAD).DistinguishedName | |
#Passing these bound parameters to another function which needs the Path | |
$PSBoundParameters.add("Path",$Path) | |
[void]($PSBoundParameters.remove("Name")) | |
} | |
Catch { | |
Throw $_ | |
} | |
#display to top level container and then get children | |
$top = @" | |
$([char]0x1b)[1;4;92m$Path$([char]0x1b)[0m | |
$([char]0x2502) | |
"@ | |
$top | |
#get child OUs | |
Get-OUTree @PSBoundParameters | |
#display a footer | |
$tz = Get-TimeZone | |
if ((Get-Date).IsDaylightSavingTime()) { | |
$tzname = $tz.daylightName | |
} | |
else { | |
$tzname = $tz.StandardName | |
} | |
$date = Get-Date -format g | |
$footer = @" | |
$([char]0x1b)[38;5;191mOrganizationl Units$([char]0x1b)[0m | |
$([char]0x1b)[38;5;199mProtected from Deletion$([char]0x1b)[0m | |
$([char]0x1b)[38;5;3mContainers$([char]0x1b)[0m | |
$([char]0x1b)[38;5;211mOther$([char]0x1b)[0m | |
$([char]0x1b)[38;5;11m$date $tzname$([char]0x1b)[0m | |
"@ | |
$footer | |
} | |
else { | |
Write-Host "This command should be run in a PowerShell Console host." -ForegroundColor magenta | |
} | |
Write-Verbose "Ending $($myinvocation.MyCommand)" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This function is described at https://jdhitsolutions.com/blog/active-directory/8173/climbing-trees-in-powershell/. It needs to be run in a console host that supports ANSI and special characters.