# Audit existing passwords

Once you have the application up and running, it can be useful to audit user's current passwords stored in the Active Directory, to see if any of them are in the compromised password store.

This process involves using a PowerShell cmdlet to extract the users password hashes from the Active Directory, and checking to see if a match is found in the compromised password store. This procedure can only check for compromised passwords, and cannot perform length and complexity validation, as those processes rely on having access to the user's plain-text password, which is not stored in Active Directory.

In order to perform this operation, you need to have the `Replicate Directory Changes All` permission on the domain object, or be a member of the `Domain Admins` group, which has this permission by default.

**This is a security-sensitive operation, and should only be performed from a trusted device, such as the domain controllers themselves**

You'll need the PowerShell module installed, and utilize the [Test‐IsADUserPasswordCompromised ](/password-protection/advanced-help/powershell-reference/test-isaduserpasswordcompromised.md)cmdlet.

The following script will create a CSV file of each user with a compromised password

```powershell
Import-Module LithnetPasswordProtection

$file = "get-pwned-users.csv";

"accountName,UPN,pwdLastSet,lastLogin,accountDisabled" | out-file $file

$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"

$Searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
$Attributes = @("PwdLastSet","lastLogonTimeStamp", "userAccountControl", "userPrincipalName", "name")
ForEach($Attribute In $Attributes)
{
    $Searcher.PropertiesToLoad.Add($Attribute) > $Null
}

$Results = $null
$Total = 0
$NumChanged = 0

$Searcher.FindAll() | % {
    $user = $_.Properties["UserPrincipalName"][0]

    if ($user -eq $null)
    {
        write-warning "User $($_.Properties["Name"][0]) has a null UPN";
        return;
    }

    $result = Test-IsADUserPasswordCompromised -UPN $user -server localhost 
    
    $pwdLastSet = $null
    $lastLogin = $null
    $disabled = $false;

    if ($_.Properties["PwdLastSet"][0] -gt 0)
    {
        $pwdLastSet = [DateTime]::FromFileTimeUtc($_.Properties["pwdLastSet"][0]).ToLocalTime()
    }

    if ($_.Properties["lastLogonTimeStamp"][0] -gt 0)
    {
        $lastLogin = [DateTime]::FromFileTimeUtc($_.Properties["lastLogonTimeStamp"][0]).ToLocalTime()
    }

    if (($_.Properties["userAccountControl"][0] -band 2) -eq 2)
    {
        $disabled = $true;
    }

    if ($result -ne $true)
    {  
        return;
    }

    $message = "$($_.Properties["Name"][0]),$user,$pwdLastSet,$lastLogin,$disabled"
    Write-Output $message
    $message | out-file $file -Append
} 
```

Using the results of the script, you could contact the users and ask them to change their password, or force a password change at next logon.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.lithnet.io/password-protection/auditing/audit-existing-passwords.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
