Blog · Systems · 7 min read

Active Directory Cleanup Without Breaking Anything

The two attributes that tell you what is really stale, why one of them lies, and the order to work in so that a mistake costs two seconds instead of a restore.

By Marko Ristic, Editor Published Aug 26, 2026 · Updated Sep 8, 2026

Every directory that has been running for a decade holds accounts for people who left, computers that were scrapped, and groups that nobody remembers creating. None of it causes an outage, which is exactly why none of it gets cleaned up.

Why it matters

A disabled account is a control. A stale enabled account with a password that never expires is an unmonitored way in. Attackers look for these first, because nobody notices a sign in on an account nobody uses.

Step 1Finding what is actually stale

Two attributes carry the answer, and they behave differently.

lastLogonTimestamp replicates between domain controllers and is what a cleanup should use. It is deliberately imprecise, updated only when the previous value is more than nine to fourteen days old, so an account that shows thirty days idle might be twenty. That is fine for finding things dormant for a year.

lastLogon is exact and does not replicate, so it is only correct on the domain controller that handled that sign in. Reading it from one controller and concluding an account is dormant is the classic mistake, and it deletes accounts that are in daily use somewhere else.

# Users with no sign in for 180 days, still enabled
Search-ADAccount -AccountInactive -TimeSpan 180.00:00:00 -UsersOnly | Where-Object Enabled

# Computers, same question
Search-ADAccount -AccountInactive -TimeSpan 180.00:00:00 -ComputersOnly

# Passwords that never expire, which is the finding that matters most
Get-ADUser -Filter {PasswordNeverExpires -eq $true -and Enabled -eq $true}

PriorityStart with the ones that can do damage

A list of four hundred stale accounts is not a work queue, it is a reason to put the job off. Sort it once and the first afternoon is worth more than the rest of the project.

The accounts to take first are stale and privileged: members of Domain Admins, Enterprise Admins, Account Operators and Backup Operators, and anything with delegated rights on an organizational unit.

An unused account with no rights is untidy. An unused account that can reset passwords across the domain is the one an attacker is looking for, and nobody is watching it precisely because nobody uses it.

# Accounts that are or were privileged, whether or not they still are
Get-ADUser -LDAPFilter "(admincount=1)" -Properties adminCount,lastLogonTimestamp,Enabled

That filter finds more than the current membership does, and the reason is worth knowing. When an account joins a protected group, the directory stamps adminCount to 1 and replaces the permissions on the object with a copy taken from AdminSDHolder. Removing the account from the group later does not undo either.

So a support account that spent a week in Domain Admins in 2019 still carries the stamp, still has permissions that do not inherit from its organizational unit, and still does not appear in any report based on current group membership.

Those are the accounts to disable first and to look hardest at, because the paperwork says they are ordinary and the directory does not.

MethodThe order that makes it safe

Deleting an object in Active Directory is not reversible in any convenient way, and the objects that look most disposable are often the ones a service depends on. The order below is slow on purpose.

Disable, do not delete. A disabled account that turns out to be needed is a two second fix. A deleted one means a restore, and the group memberships it held are gone even then.

Move disabled objects to a holding organizational unit. One place to look, one place to empty, and a Group Policy scope that applies to nothing.

Wait a full business cycle. Thirty days catches monthly processes. Ninety catches quarterly ones, and quarterly is where the surprises live: the account that only signs in to run the quarter end job.

Then delete, in batches, with an export taken first. A system state backup of a domain controller restores the directory and is the wrong tool for one wrong deletion.

An LDIF export of the holding organizational unit is a file, takes seconds, and holds every attribute of everything about to go. Take it, keep it with the change record, and the one time it is needed nothing else will do.

# Everything in the holding OU, attributes and all, into one file
ldifde -f disabled-2026-09.ldf -d "OU=Disabled,DC=example,DC=com" -p subtree
ObjectLook forBefore touching it
User accountsInactive 180 days, still enabledCheck for service use in logs
Computer accountsInactive 90 daysConfirm the machine is really gone
GroupsEmpty, and not referencedSearch file server permissions
Service accountsPassword never expiresFind what runs as it, first
Organizational unitsEmpty, no policy linkedCheck delegation on it

The trapWhat a computer account is carrying

A computer account looks like the safest thing in the directory to delete. The machine is scrapped, the object is dead weight, and nothing signs in as it. That is true right up until somebody needs the disk.

Several things are stored as part of the computer object rather than alongside it, and they go when it goes.

BitLocker recovery keys. When recovery information is escrowed to the directory it is written as a child object under the computer account. Delete the computer and the recovery key for that machine is gone, along with any chance of reading the drive that is still sitting in a cupboard waiting to be wiped or read one last time.

The local administrator password. Both the original and the current LAPS store the managed password as an attribute on the computer object. A machine that is genuinely gone does not care, and a machine that turns out to be in a drawer somewhere now has a local password nobody holds.

Certificates and the trust itself. Deleting an account and rejoining the same machine later gives it a new security identifier, so anything granted to the old one has to be granted again.

The rule that follows is short. Before deleting a computer account, export its BitLocker recovery information and its LAPS password to wherever those are kept for retired hardware, and only then delete the object. The disable and wait step exists for exactly this: a computer account disabled for a quarter costs nothing and keeps both.

GroupsWhy an empty one can still grant access

Groups are the objects most likely to look disposable and most likely to be load bearing, and there are three separate reasons a group that appears empty is not.

What it looks likeWhat is actually trueHow to check
No members listedMembers whose primary group it is are not listed in the member attributeQuery primaryGroupID for the group's RID
No members listedIt is a member of another group that does have membersRead memberOf on the group itself
Nobody uses itIts SID is written into an access control list on a file server or an applicationSearch the ACLs for the SID, not the name
Recreating it would fix a mistakeA new group with the same name has a different SID, so every ACL entry still points at the old oneRestore the object rather than recreate it

The primary group case catches people the most often. The member attribute does not list an account whose primaryGroupID points at that group, so a group can be someone's primary group and read as empty in every tool that lists members.

The search that matters is on the security identifier rather than the name. When a group is deleted, the entries it left behind on file servers and applications do not disappear, they turn into an unresolved identifier that most permission dialogs display as a raw string and most people ignore.

That is the trace to look for before deleting, and the mess to inherit after.

The hard partService accounts

These are where a cleanup goes wrong, because a service account signs in rarely, holds more privilege than it needs, and has a password set in 2014 that never expires. Every attribute that makes it look stale is also what makes it dangerous to disable.

Before touching one, find out what runs as it. Scheduled tasks, Windows services, application pools, and database connection strings are the usual four, and only the first two are easy to search. The account name in a sign in log, with the source address, tells you which machine to look at.

The right end state for the ones that survive is a managed service account, where the password rotates automatically and nobody knows it. That is a project rather than a cleanup task, but the cleanup is when you find out how many you need.

AfterwardsKeeping it clean

A cleanup that is not automated happens once. Three things keep the number near zero without anybody remembering to look.

A joiners and leavers process that actually disables accounts on the last day rather than eventually. This is the whole problem at the source, and everything else is compensation for not having it.

A scheduled report of accounts inactive for ninety days, sent to a person rather than a mailbox nobody opens. A short list monthly is read. A long list quarterly is not.

A policy that new service accounts are managed service accounts. It does not fix the existing ones, and it stops the pile growing while you work through them.

  1. Query on lastLogonTimestamp, never lastLogon

    The exact attribute does not replicate, so reading it from one domain controller marks accounts as dormant that are in daily use against another.

  2. Disable and move to a holding organizational unit

    One place to look and one place to empty. A disabled account that turns out to be needed is a two second fix, where a deleted one is a restore.

  3. Wait a full quarter before deleting anything

    Thirty days catches monthly processes. Ninety catches the account that only signs in to run the quarter end job, which is where the surprises live.

  4. Handle service accounts separately, and last

    Everything that makes them look stale is what makes them dangerous. Find what runs as each one before disabling it, then move the survivors to managed service accounts.

The three thresholds

Round numbers, chosen so that a quarterly process gets a chance to run before anything is removed.

180 duser accounts inactive
90 dcomputer accounts inactive
90 ddisabled before deletion

FAQQuestions from the comments

Why does lastLogon disagree between domain controllers?

Because it is not replicated. Each controller records only the sign ins it handled, so the value is correct locally and misleading everywhere else.

Is the Active Directory Recycle Bin enough of a safety net?

It helps a great deal and is worth enabling, but it has a limited lifetime and restores an object rather than everything that referenced it. It is a reason to be careful, not a reason not to be.

How do I find what a service account is used for?

Scheduled tasks and Windows services are searchable. Application pools and connection strings are not, so read the sign in logs for the account and go to the source machine.

Should computer accounts be deleted as aggressively as user accounts?

They can be, and ninety days is a common threshold, because a machine that has not authenticated in a quarter is almost always gone. Confirm against the asset register first.

What about accounts with passwords that never expire?

That is the finding worth acting on first, whether or not the account is stale. It is the combination of unmonitored and permanent that attackers look for.

What happens to BitLocker recovery keys when a computer account is deleted?

They go with it. Escrowed recovery information is stored as a child object under the computer account, so deleting the account deletes the only copy the organization holds. Export it before the deletion, or leave the account disabled, which costs nothing and keeps it.

Why does an account still have odd permissions after I removed it from Domain Admins?

Because joining a protected group stamps adminCount to 1 and replaces the permissions on the object with a copy from AdminSDHolder, and leaving the group undoes neither. The account keeps permissions that do not inherit from its organizational unit and stops appearing in any report built on current group membership.

A group shows no members. Is it safe to delete?

Not on that evidence. The member attribute does not list anyone whose primary group it is, the group may be a member of another group that does have members, and its security identifier may be written into an access control list on a file server. Check all three.

Can I recreate a group I deleted by mistake?

You can create one with the same name, and it will not work. Permissions are granted to the security identifier, and a new object has a new one, so every existing entry still points at the group that is gone. Restore the original from the recycle bin or from the export instead.

Does cleaning up group memberships fix anything besides tidiness?

Occasionally something concrete. Kerberos carries group membership in the ticket, and an account in enough groups produces a ticket too large for the default buffer, which shows up as a user who cannot sign in to one application while everything else works. Removing memberships nobody needs is the fix, and it is usually found the hard way.

How long should objects sit in the holding organizational unit?

A full quarter, because the surprises are quarterly. The account that exists to run a quarter end job signs in four times a year and looks identical to an abandoned one for eighty nine days out of ninety.