PowerShell Active Directory OU Structure: Create and Manage OUs

PowerShell Active Directory OU Structure: Create and Manage OUs

PowerShell Tips Editor 3 min read
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 $false on 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 -Path parameter to New-ADOrganizationalUnit requires a correctly formatted DN. A typo in the DN throws “The object does not exist.” Verify the parent DN with Get-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.

Send-Item -To