Wes Ellis./ a personal notebook
Technology. Stories. Side projects.
A few things worth writing down.
← Back to Script Library

SCRIPT LIBRARY · POWERSHELL

Delegating Your Tier 0 OU to a Dedicated Admin Group with PowerShell

Create a Tier 0 admin group, keep it where only Tier 0 can touch it, and give it full control of the Tier 0 OU. Preview the whole thing with -WhatIf first.

AT A GLANCEGrant-Tier0Delegation.ps1
What it does
Creates a Tier 0 admin group inside the Tier 0 OU, adds the admin accounts you name, and grants the group inheritable Full Control over the Tier 0 OU. Safe to re-run, and -WhatIf shows every step first.
Requires
  • Windows PowerShell 5.1 or PowerShell 7 on Windows
  • ActiveDirectory module (RSAT)
Permissions
Rights to create groups in the target OU and to change permissions on the Tier 0 OU. In practice that's a Domain Admin, run from a Tier 0 admin workstation.
Runs on
Windows 10/11 with RSAT, Windows Server 2016+
Tested
Parse-checked and dry-run with -WhatIf against mocked AD cmdlets in PowerShell 7.4. The ACL step needs a real domain to exercise.

Part 1 of the thread Keeping Active Directory tidy

If you've read anything about securing Active Directory in the last decade, you've run into tiering. Microsoft's current name for it is the enterprise access model, but the core idea hasn't changed: the stuff that controls identity (domain controllers, AD itself, Entra Connect, your PKI, and every account and group that can manage those) is Tier 0, and nothing from a lower tier gets to control it.

The part people skip is the plumbing. You make a Tier 0 OU, then everybody who needs to manage it just... uses Domain Admins. This script does the boring, correct thing instead. It creates a dedicated group, puts that group inside Tier 0 so only Tier 0 admins can change who's in it, and delegates the Tier 0 OU to it.

The OU, group name, members and domain are all parameters, there are no credentials anywhere in it, and it supports -WhatIf, so you can see exactly what it would do before it touches anything.

Grant-Tier0Delegation.ps1Download
<#
.SYNOPSIS
    Creates a Tier 0 admin group and delegates full control of the Tier 0 OU tree to it.
.DESCRIPTION
    Builds the delegation piece of a tiered admin model: one security group whose members
    manage everything under the Tier 0 OU. The script creates the group (inside Tier 0, so
    only Tier 0 can change who's in it), adds any members you name, and adds an inheritable
    Full Control entry to the Tier 0 OU's ACL. It's safe to re-run: existing groups, members
    and permissions are left alone. Supports -WhatIf.
.PARAMETER Tier0OU
    Distinguished name of the Tier 0 OU, e.g. OU=Tier 0,OU=Admin,DC=contoso,DC=com.
.PARAMETER GroupName
    Name of the delegation group. Default: Tier0-Admins.
.PARAMETER GroupOU
    Where to create the group. Defaults to OU=Groups under the Tier 0 OU, falling back to the Tier 0 OU itself.
.PARAMETER Member
    Accounts to add to the group (sAMAccountName or DN). Use dedicated admin accounts, not daily-driver ones.
.PARAMETER Server
    Domain or domain controller to talk to. Defaults to the domain of the computer you're running on.
.EXAMPLE
    .\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -WhatIf
.EXAMPLE
    .\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -Member adm-t0-jdoe, adm-t0-asmith
#>
[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory)][ValidatePattern('^OU=.+DC=')][string]$Tier0OU,
    [ValidateNotNullOrEmpty()][string]$GroupName = 'Tier0-Admins',
    [string]$GroupOU,
    [string[]]$Member,
    [string]$Server
)

$ErrorActionPreference = 'Stop'
Import-Module ActiveDirectory
$ad = @{}
if ($Server) { $ad.Server = $Server }

function Write-Result([string]$Action, [string]$Target, [string]$Result, [string]$Detail = '') {
    [pscustomobject]@{ Action = $Action; Target = $Target; Result = $Result; Detail = $Detail }
}

# --- Check the OU and decide where the group lives
$null = Get-ADOrganizationalUnit -Identity $Tier0OU @ad
if (-not $GroupOU) {
    $candidate = "OU=Groups,$Tier0OU"
    $GroupOU = if (Get-ADOrganizationalUnit -Filter * -SearchBase $Tier0OU -SearchScope OneLevel @ad | Where-Object DistinguishedName -eq $candidate) { $candidate } else { $Tier0OU }
}
if ($GroupOU -notlike "*$Tier0OU") {
    Write-Warning "$GroupOU is outside $Tier0OU. Whoever controls that OU can add themselves to your Tier 0 group."
}

# --- 1. The group
$group = Get-ADGroup -Filter "Name -eq '$($GroupName -replace "'", "''")'" @ad
if ($group) {
    Write-Result 'Create group' $GroupName 'Skipped' "Already exists at $($group.DistinguishedName)"
}
elseif ($PSCmdlet.ShouldProcess("$GroupName in $GroupOU", 'Create security group')) {
    $group = New-ADGroup -Name $GroupName -SamAccountName $GroupName -GroupCategory Security -GroupScope Global -Path $GroupOU -Description "Full control of $Tier0OU (tiered admin model)" -PassThru @ad
    Write-Result 'Create group' $GroupName 'Done'
}
else {
    Write-Result 'Create group' $GroupName 'WhatIf'
}

# --- 2. Members
foreach ($m in $Member) {
    if (-not $group) { Write-Result 'Add member' $m 'WhatIf' 'Group would be created first'; continue }
    try {
        if ($PSCmdlet.ShouldProcess($GroupName, "Add member $m")) {
            Add-ADGroupMember -Identity $group -Members $m @ad
            Write-Result 'Add member' $m 'Done'
        }
        else { Write-Result 'Add member' $m 'WhatIf' }
    }
    catch { Write-Result 'Add member' $m 'Failed' $_.Exception.Message }
}

# --- 3. Delegate Full Control on the Tier 0 OU, inherited by everything below it
if (-not $group) {
    Write-Result 'Delegate Full Control' $Tier0OU 'WhatIf' 'Group would be created first'
    return
}
$sid  = [System.Security.Principal.SecurityIdentifier]$group.SID.Value
$ou   = Get-ADObject -Identity $Tier0OU -Properties nTSecurityDescriptor @ad
$acl  = $ou.nTSecurityDescriptor
$full = [System.DirectoryServices.ActiveDirectoryRights]::GenericAll
$have = $acl.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]) | Where-Object {
    $_.IdentityReference -eq $sid -and ($_.ActiveDirectoryRights -band $full) -eq $full -and $_.InheritanceType -eq 'All'
}
if ($have) {
    Write-Result 'Delegate Full Control' $Tier0OU 'Skipped' 'Permission already present'
}
elseif ($PSCmdlet.ShouldProcess($Tier0OU, "Grant $GroupName Full Control (this OU and all descendants)")) {
    $rule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new($sid, 'GenericAll', 'Allow', [System.DirectoryServices.ActiveDirectorySecurityInheritance]::All)
    $acl.AddAccessRule($rule)
    Set-ADObject -Identity $Tier0OU -Replace @{ nTSecurityDescriptor = $acl } @ad
    Write-Result 'Delegate Full Control' $Tier0OU 'Done'
}
else {
    Write-Result 'Delegate Full Control' $Tier0OU 'WhatIf'
}

Parameters

ParameterTypeDefaultWhat it's for
-Tier0OUstring—Distinguished name of your Tier 0 OU. Required, and it has to exist already.
-GroupNamestringTier0-AdminsWhat to call the delegation group. If it already exists, the script uses it.
-GroupOUstringOU=Groups under the Tier 0 OUWhere the group gets created. Falls back to the Tier 0 OU itself if there's no Groups OU. You'll get a warning if you point it outside Tier 0.
-Memberstring[]—Admin accounts to add, by sAMAccountName or DN. These should be separate Tier 0 admin accounts, not anyone's everyday login.
-Serverstring—Domain name or a specific domain controller. Leave it off to use the domain you're joined to.
-WhatIfswitch—Show what would be created and granted without changing anything.

Run it

See the plan first. Nothing gets created.

.\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -WhatIf

Create the group, add two admin accounts, and delegate the OU.

.\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -Member adm-t0-jdoe, adm-t0-asmith

Use your own naming convention and a specific DC.

.\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -GroupName 'GG-T0-Operators' -Server dc01.contoso.com

Keep a record of what it did.

.\Grant-Tier0Delegation.ps1 -Tier0OU 'OU=Tier 0,OU=Admin,DC=contoso,DC=com' -Member adm-t0-jdoe | Export-Csv .\tier0-delegation.csv -NoTypeInformation

What you'll see

Example outputvalues are illustrative
Action                Target                               Result  Detail
------                ------                               ------  ------
Create group          Tier0-Admins                         Done
Add member            adm-t0-jdoe                          Done
Add member            adm-t0-asmith                        Failed  Cannot find an object with identity: 'adm-t0-asmith'
Delegate Full Control OU=Tier 0,OU=Admin,DC=contoso,DC=com Done

How it works

  1. Check the target. It confirms the Tier 0 OU exists, then decides where the group lives. A Groups OU under Tier 0 is the default. If you point it somewhere outside Tier 0, it warns you, because whoever controls that other OU could quietly add themselves to your Tier 0 group.
  2. Create the group, or reuse it. A global security group with a description that says what it's for. If a group by that name already exists, it's left alone.
  3. Add members. Each one on its own, so a typo in one account name doesn't stop the rest.
  4. Delegate the OU. It reads the OU's security descriptor, checks whether the group already has Full Control inherited to everything below, and if not, adds one access rule and writes it back with Set-ADObject. Using the AD cmdlets for this (rather than the AD: drive) means -Server works for the whole run.
  5. Report. Every step returns an object with Done, Skipped, Failed or WhatIf, so you can read it on screen or export it.

Under -WhatIf it still reads from AD, so the preview is accurate. It just doesn't create or grant anything, and it tells you when a later step depends on an earlier one that hasn't happened yet.

Take it further

  • Build the rest of the tiers. The same pattern works for Tier 1 servers and Tier 2 workstations and users. There's a Tier 2 version with narrower permissions.
  • Stop Tier 0 accounts from wandering. Add your Tier 0 admin accounts to Protected Users and use authentication policies or "Deny log on" rights so they can't sign in to lower-tier machines.
  • Audit it on a schedule. A monthly Get-ADGroupMember Tier0-Admins compared against a known-good list catches the "just for this weekend" additions that never got removed.

Things that'll trip you up

  • AdminSDHolder wins on protected accounts. Anything that's a member of Domain Admins, Enterprise Admins and the other protected groups gets its permissions reset to the AdminSDHolder template every hour, with inheritance turned off. Your delegated Full Control won't reach those objects no matter where they sit. That's by design, so don't fight it.
  • Membership in this group is Tier 0. Treat it that way. Full control of the OU that holds your Tier 0 servers and accounts is a short walk from owning the domain. Only dedicated Tier 0 admin accounts go in, and they only sign in to Tier 0 machines.
  • Run it from the right place. Building your Tier 0 delegation from a daily-driver laptop, signed in with the account you read email on, undoes a good chunk of the point. Use a privileged access workstation or at least a hardened admin box.
  • There's no undo switch. Re-running is safe because it skips anything that's already there, but it doesn't remove anything. To back out, delete the group's entry on the OU's Security tab (Advanced) in Active Directory Users and Computers, or with dsacls.