Query Details

Active Directory - Password Security Posture Assessment

MDI Identity Password Security Posture Assessment

Query

let accountinfo = IdentityAccountInfo
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated,*) by IdentityId
| extend DaysSinceLastPasswordChange =
    iff(isnull(LastPasswordChangeTime), int(null),
        datetime_diff('day', now(), LastPasswordChangeTime))
| extend YearsSinceLastPasswordChange =
    iff(isnull(LastPasswordChangeTime), int(null),
        datetime_diff('year', now(), LastPasswordChangeTime))   
| extend Sensitive = array_index_of(Tags, "Sensitive")   != -1
| extend SensitiveLabel = iff(Sensitive == 1, "🟥 Sensitive", "⬜ Not Sensitive")
| project IdentityId,AccountUpn, AccountStatus, LastPasswordChangeTime,DaysSinceLastPasswordChange,YearsSinceLastPasswordChange, Sid, Sensitive, SensitiveLabel;
let IdInfo = IdentityInfo
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated,*) by IdentityId
| extend PasswordNeverExpires = array_index_of(UserAccountControl, "PasswordNeverExpires")   != -1,
         PasswordNotRequired = array_index_of(UserAccountControl, "PasswordNotRequired")   != -1
| extend OUPath = extract(@"CN=[^,]+,(.*)", 1, DistinguishedName)
| project IdentityId,AccountName, AccountDomain, AccountDisplayName, OnPremSid, OnPremObjectId, AccountUpn, PasswordNeverExpires, PasswordNotRequired,Type, OUPath, IdentityEnvironment;
IdInfo
| join kind=leftouter (accountinfo)
on $left. IdentityId == $right. IdentityId
| project IdentityId, AccountName, AccountStatus,AccountDomain, AccountDisplayName,AccountUpn,Sensitive,SensitiveLabel,LastPasswordChangeTime, DaysSinceLastPasswordChange, YearsSinceLastPasswordChange, PasswordNeverExpires, PasswordNotRequired,Type, OUPath, IdentityEnvironment
| sort by DaysSinceLastPasswordChange desc 
//| where AccountStatus != @"Disabled"
| where Type == @"ServiceAccount"
| where IdentityEnvironment != "Cloud"

About this query

Explanation

This KQL query is designed to assess the security posture of Active Directory accounts by examining password-related attributes. Here's a simplified breakdown of what the query does:

  1. Data Sources: It uses two tables, IdentityAccountInfo and IdentityInfo, from Microsoft Defender XDR to gather information about Active Directory accounts.

  2. Identify Stale Passwords: The query calculates how long it has been since each account's password was last changed, both in days and years. This helps identify accounts with outdated passwords.

  3. Assess Account Sensitivity: It checks if accounts are marked as sensitive or have elevated privileges, which could pose a higher security risk.

  4. Review Password Policies: It identifies accounts with specific flags like PasswordNeverExpires or PasswordNotRequired, which might indicate weaker security settings.

  5. Analyze Account Status: The query focuses on enabled accounts, as these are actively used and could pose security risks if compromised.

  6. Special Focus on High-Risk Accounts: A variant of the query specifically filters for high-risk accounts, such as built-in accounts (Administrator, Guest, krbtgt) and Microsoft Entra Connect synchronization accounts.

  7. Visualize Password Age: The query can be extended to visualize the distribution of accounts based on the age of their passwords, categorizing them into buckets like "0-1 years", "1-2 years", etc.

  8. Logon Activity: It can also be extended to include the last logon date from Active Directory or Entra ID, helping to identify accounts that haven't been used recently.

Overall, this query helps security teams identify potential vulnerabilities related to password management and account configurations in Active Directory, aiding in proactive security posture assessments.

Details

Alex Verboon profile picture

Alex Verboon

Released: June 2, 2026

Tables

IdentityAccountInfoIdentityInfoIdentityLogonEventsSigninLogs

Keywords

ActiveDirectoryAccountsPasswordSecurityRiskMicrosoftDefenderIdentityTeamsPoliciesEntraConnectSynchronization

Operators

letwhereagosummarizearg_maxbyextendiffisnulldatetime_diffarray_index_ofprojectextractjoinkindonsortdesccaseisnotnulldcountrendercolumnchartinstartswithtolowermax_ofisempty

Actions

GitHub