PowerShell Active Directory OU Structure: Create and Manage OUs

Building a new domain or restructuring an existing one means creating dozens of Organizational Units in a specific hierarchy. Doing that manually through ADUC is tedious and error-prone. Scripting your PowerShell Active Directory OU structure means the hierarchy is reproducible, documented in code, and consistent across dev, test, and production environments — with a single script execution.
Quick Answer / TL;DR
Use New-ADOrganizationalUnit to create OUs and Get-ADOrganizationalUnit to list them. Protect against deletion with -ProtectedFromAccidentalDeletion $true. Move objects with Move-ADObject.
Get All OUs with Get-ADOrganizationalUnit
Get-ADOrganizationalUnit returns all OUs in the domain when filtered with -Filter *. Export the structure before making changes or include it in your configuration audit reports.
# List all OUs
Get-ADOrganizationalUnit -Filter * |
Select-Object Name, DistinguishedName |
Sort-Object DistinguishedName |
Format-Table -AutoSize
# List OUs directly under a specific parent
Get-ADOrganizationalUnit -SearchBase 'OU=Employees,DC=contoso,DC=com' `
-SearchScope OneLevel -Filter * |
Select-Object Name, DistinguishedName
Create a New OU
New-ADOrganizationalUnit creates an OU. The -Path parameter specifies the parent container as a Distinguished Name (DN). Use -ProtectedFromAccidentalDeletion $true for any OU that should not be deleted accidentally — this is enabled by default and must be explicitly disabled before deletion.
# Create a top-level OU
New-ADOrganizationalUnit -Name 'Employees' `
-Path 'DC=contoso,DC=com' `
-Description 'All employee accounts'
# Create a child OU
New-ADOrganizationalUnit -Name 'IT' `
-Path 'OU=Employees,DC=contoso,DC=com' `
-Description 'IT Department'
# Verify creation
Get-ADOrganizationalUnit -Filter "Name -eq 'IT'" | Select-Object Name, DistinguishedName
Protect OU Against Accidental Deletion
By default, New-ADOrganizationalUnit creates OUs with ProtectedFromAccidentalDeletion set to $true. This prevents deletion via ADUC or PowerShell without first explicitly disabling the protection. To delete a protected OU, you must remove the protection first.
# Create with explicit protection (same as default)
New-ADOrganizationalUnit -Name 'Finance' `
-Path 'OU=Employees,DC=contoso,DC=com' `
-ProtectedFromAccidentalDeletion $true
# Disable protection to allow deletion
Set-ADOrganizationalUnit -Identity 'OU=Finance,OU=Employees,DC=contoso,DC=com' `
-ProtectedFromAccidentalDeletion $false
# Now deletion is possible
Remove-ADOrganizationalUnit -Identity 'OU=Finance,OU=Employees,DC=contoso,DC=com' -Confirm:$false
Create Nested OU Structure
Build an entire OU hierarchy from an array definition. This script creates a standard structure in one execution — ideal for new domain builds or environment provisioning scripts.
# Define the OU hierarchy
$ouStructure = @(
@{ Name = 'Employees'; Path = 'DC=contoso,DC=com' },
@{ Name = 'IT'; Path = 'OU=Employees,DC=contoso,DC=com' },
@{ Name = 'Finance'; Path = 'OU=Employees,DC=contoso,DC=com' },
@{ Name = 'HR'; Path = 'OU=Employees,DC=contoso,DC=com' },
@{ Name = 'ServiceAccounts'; Path = 'DC=contoso,DC=com' },
@{ Name = 'Workstations'; Path = 'DC=contoso,DC=com' }
)
foreach ($ou in $ouStructure) {
if (-not (Get-ADOrganizationalUnit -Filter "DistinguishedName -eq 'OU=$($ou.Name),$($ou.Path)'" -ErrorAction SilentlyContinue)) {
New-ADOrganizationalUnit -Name $ou.Name -Path $ou.Path
Write-Host "Created: OU=$($ou.Name),$($ou.Path)"
} else {
Write-Host "Exists: OU=$($ou.Name),$($ou.Path)"
}
}
Move Users Between OUs
Move-ADObject moves any AD object (user, computer, group) to a new parent container. The -TargetPath must be the DN of the destination container. Bulk-move users filtered by department or any other attribute.
# Move a single user to a new OU
$user = Get-ADUser -Identity 'jsmith'
Move-ADObject -Identity $user.DistinguishedName `
-TargetPath 'OU=IT,OU=Employees,DC=contoso,DC=com'
# Bulk-move all Finance users to the Finance OU
$financeUsers = Get-ADUser -Filter "Department -eq 'Finance'"
$targetOU = 'OU=Finance,OU=Employees,DC=contoso,DC=com'
$financeUsers | ForEach-Object {
Move-ADObject -Identity $_.DistinguishedName -TargetPath $targetOU
Write-Host "Moved: $($_.Name)"
}
Delete an OU
Delete an OU and all its contents with Remove-ADOrganizationalUnit. Always disable protection first. Use -Recursive to also delete child objects. Verify with Test-Path equivalent or a pre-deletion check before removing.
# Safe OU deletion workflow
$ouDN = 'OU=LegacyUsers,DC=contoso,DC=com'
# Check object count inside the OU
$count = (Get-ADObject -SearchBase $ouDN -SearchScope Subtree -Filter *).Count
Write-Host "Objects in OU: $count"
if ($count -eq 0) {
# Disable protection first
Set-ADOrganizationalUnit -Identity $ouDN -ProtectedFromAccidentalDeletion $false
# Delete empty OU
Remove-ADOrganizationalUnit -Identity $ouDN -Confirm:$false
Write-Host 'OU deleted'
} else {
Write-Warning "OU contains $count objects — move them first"
}
Common Errors and Fixes
- Cannot delete OU with ProtectedFromAccidentalDeletion — disable first. Attempting to delete a protected OU throws “Access is denied” or “The object cannot be deleted.” Run
Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $falseon the OU before deletion. This applies to all OUs including ones created through the GUI. - Distinguished Name format must be exact for path parameter. The
-Pathparameter toNew-ADOrganizationalUnitrequires a correctly formatted DN. A typo in the DN throws “The object does not exist.” Verify the parent DN withGet-ADOrganizationalUnit -Filter "Name -eq 'ParentName'"before using it as a path.
Related Cmdlets / See Also
Wrapping Up
OU management in PowerShell is straightforward: create with New-ADOrganizationalUnit, protect with ProtectedFromAccidentalDeletion, move objects with Move-ADObject, and delete carefully after disabling protection. Script your domain structure from an array definition so it is repeatable, version-controlled, and consistent across every environment you build.


