This post explains how to configure Active Directory (on-premises and hybrid environments) to meet the Compliance Framework requirement IA.L2-3.5.6 — automatically disabling inactive identifiers — with concrete, actionable steps, a sample PowerShell implementation, and operational guidance for small businesses pursuing NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 compliance.
Why automatic disabling matters and setting your policy
At a high level, IA.L2-3.5.6 requires organizations to ensure inactive accounts (identifiers) are disabled automatically so they cannot be used as attack vectors; this supports least-privilege and reduces orphaned-account risk. The Compliance Framework expects documented policy (thresholds, exceptions, roles) and technical enforcement. Before you implement anything, define what "inactive" means for your organization: common choices are 30 days for privileged accounts, 60 days for contractors, and 90 days for general user accounts, but your policy must be written, justified and consistent with your risk assessment.
Inventory, exclusions, and tagging — planning for safe automation
Automation must not break applications or service accounts. Start by inventorying all account types: interactive user accounts, service/application accounts, managed service accounts (gMSA), cloud-only accounts and accounts used by sync tools (Azure AD Connect). Tag or place exceptions in a DoNotDisable security group or by an attribute (e.g., extensionAttribute1="ServiceAccount") so the automation can safely skip them. Document an approval workflow for exceptions (owner, justification, expiration) and ensure HR/IT onboarding/offboarding are integrated with this inventory so accounts used for long-running tasks are classified correctly.
Technical note on "last logon" data
Active Directory provides several attributes: lastLogon (per-DC, very accurate), lastLogonTimestamp (replicated, but only updates when a threshold — typically 14 days — is exceeded), and various msDS attributes in newer environments. For most small businesses the replicated lastLogonTimestamp is sufficient if you accept its 14-day granularity; for strict accuracy query lastLogon across all DCs or use sign-in logs (Azure AD) where available. Plan your script to convert lastLogonTimestamp to a DateTime via [DateTime]::FromFileTime() and document the expected granularity.
Practical implementation — sample PowerShell approach (on-prem AD)
Below is a robust pattern you can deploy as a scheduled task on a management server or domain controller with RSAT installed. The script identifies enabled users whose lastLogonTimestamp is older than your threshold, excludes members of your DoNotDisable group, disables the account, moves it to a "DisabledUsers" OU, and logs the action to CSV for audit. Run weekly and tune thresholds by policy.
Import-Module ActiveDirectory
# Config
$thresholdDays = 60
$cutoff = (Get-Date).AddDays(-$thresholdDays)
$exemptGroup = "DoNotDisable" # group containing accounts to skip
$disabledOU = "OU=DisabledUsers,DC=contoso,DC=com"
$log = "C:\Logs\DisableInactiveUsers_$(Get-Date -Format yyyyMMdd).csv"
# Get exempt members
$exempt = Get-ADGroupMember -Identity $exemptGroup -Recursive | Select-Object -ExpandProperty SamAccountName
# Find candidates
Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -ne $true} -Properties LastLogonTimestamp,SamAccountName,DistinguishedName |
Where-Object {
$ldt = $_.LastLogonTimestamp
if ($ldt) { $last = [DateTime]::FromFileTime($ldt) } else { $last = $null }
($last -eq $null -or $last -lt $cutoff) -and -not ($exempt -contains $_.SamAccountName)
} | ForEach-Object {
$user = $_
Disable-ADAccount -Identity $user.DistinguishedName
Move-ADObject -Identity $user.DistinguishedName -TargetPath $disabledOU
[PSCustomObject]@{
Time = (Get-Date)
SamAccountName = $user.SamAccountName
DistinguishedName = $user.DistinguishedName
LastLogon = if ($user.LastLogonTimestamp) {[DateTime]::FromFileTime($user.LastLogonTimestamp)} else {"Never"}
} | Export-Csv -Path $log -NoTypeInformation -Append
}
Notes: run the scheduled task as a service account that has rights to disable and move accounts (delegate rather than using a full Domain Admin where possible). Ensure the management server has network access to all DCs and appropriate modules installed. Keep the script idempotent and test on a non-production OU first.
Hybrid/Azure AD considerations
If you have Azure AD or a hybrid setup with Azure AD Connect, the authoritative identity may be on-prem or in the cloud. For synced users, disable them on-premises so the change syncs to Azure AD. For cloud-only users, use Microsoft Graph (preferred) or AzureAD PowerShell to set accountEnabled = false. Example (Microsoft Graph PowerShell): Connect-MgGraph -Scopes User.ReadWrite.All; Get-MgUser -Filter "accountEnabled eq true" ... then Update with Update-MgUser -UserId <id> -AccountEnabled:$false. Ensure your automation differentiates synced vs cloud-only accounts to avoid conflicts.
Operational controls, logging, and re-enable process
Automation must be supported by processes: notify owners before disablement (email + ticket), retain logs for audit (CSV and SIEM ingestion), and require documented re-enable requests that include verification steps (HR confirmation, identity verification, MFA reenrollment). Implement a "quarantine OU" pattern (DisabledUsers) so tools and scripts can easily find disabled accounts and re-enable after approval. Keep a retention window (e.g., disabled for 30 days before deletion) aligned to your data retention policy and record all actions for compliance evidence.
Real-world small business scenario
Example: A 75-person engineering firm sets policy: 30 days for admin accounts, 60 days for employee accounts, 90 days for contractor accounts. They create a DoNotDisable group for CI/CD and backup service accounts and inventory those accounts into a spreadsheet. They deploy the above PowerShell script on a single management server, run it weekly as a scheduled task using a service account with delegated rights, and configure the script to email CSV logs to IT and the security officer. After three months the firm reduced orphaned accounts by 80% and passed a compliance spot-check because they provided policy, logs, ticketing evidence, and a re-enable workflow.
Failing to implement automated disabling increases risk of credential theft, lateral movement and long-term persistence for attackers; it also exposes you to audit failures under NIST/CMMC because orphaned and unused accounts are a common finding. Automatic disabling combined with a documented exceptions process and logs closes a major control gap and reduces attack surface.
Summary: define thresholds in policy, inventory and exempt service accounts, implement automation (PowerShell for on-prem AD; Graph for Azure AD), schedule regular runs, log and notify, and pair technical controls with HR and ticketing workflows. This combined approach delivers practical compliance with IA.L2-3.5.6 while minimizing business disruption and providing auditable evidence for NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 assessments.