'Script every AD user on every DC

I am beginner in scripting with powershell. My boss asked me to create a script that will get information about the last logon from every user in our domain on every DC. I have created the following script:

Import-Module ActiveDirectory

function Get-ADUsersLastLogon() {
    $dcs = Get-ADDomainController -Filter { Name -like "*" }
    $users = Get-ADUser -Filter * -Properties LastLogonDate | Where-Object { $_.LastLogonDate -le (Get-Date).AddDays(-30) }
    $time = 0
    $exportFilePath = "C:\output\aduser.csv"
    $columns = "name,username,datetime,domain controller,enabled"

    Out-File -filepath $exportFilePath -force -InputObject $columns

    foreach ($user in $users) {
        $Activeuser = $user.Enabled

        foreach ($dc in $dcs) { 
            $hostname = $dc.HostName
            $currentUser = Get-ADUser $user.SamAccountName | Get-ADObject -Server $hostname -Properties lastLogon
    
            if ($currentUser.LastLogon -gt $time) {
                $time = $currentUser.LastLogon
            }
        }

        $dt = [DateTime]::FromFileTime($time)
        $row = $user.Name + "," + $user.SamAccountName + "," + $dt + "," + $hostname + "," + $Activeuser
    
        Out-File -filepath $exportFilePath -append -noclobber -InputObject $row
    
        $time = 0
    }
}

Get-ADUsersLastLogon

My boss asked me to change the following things in this script:

  • the output of the domain controller is only our DC in an other country. I want to know which DC the user last logged into.
  • The running of the script is taking too long. Like half a day. Is it possible to make this faster?

I hope someone can help me with this, I tried a few things, but I didn't work :(



Solution 1:[1]

the output of the domain controller is only our DC in an other country. I want to know which DC the user last logged into.

It looks like you've already solved this by querying each DC for each user object.

The running of the script is taking too long. Like half a day. Is it possible to make this faster?

That's because you're querying each DC for each user object :)

One way of speeding it up would be to flip your query logic around - query each DC for all the users but only once, and then update the logon timestamps only when a newer one is encountered.
For that to work, you need to keep track of the highest timestamp seen for each user. I'd suggest using a simple hashtable to index the user accounts by their user name:

$dcs = Get-ADDomainController -Filter { Name -like "*" }

# create hashtable to keep track of latest timestamps per user
$userLastLogonTable = @{}

foreach($dc in $dcs){
  # fetch all users from each DC
  Get-ADUser -Filter * -Properties LastLogonDate -Server $dc | ForEach-Object {
    # Only add new timestamps to table if we either haven't seen the username before, or if the timestamp is newer than the current
    if(-not $userLastLogonTable.Contains($_.SAMAccountName) -or $userLastLogonTable[$_.SAMAccountName].LastLogonDate -lt $_.LastLogonDate){
      $userLastLogonTable[$_.SAMAccountName] = [pscustomobject]@{
        LastLogonDate = $_.LastLogonDate
        LogonServer   = $dc.Name
      }
    }
  }
}

# Now that we have a complete table of all users and their last logon timestamp, 
# we can then easily identify usernames that have no recent logons
$staleUserNames = $userLastLogonTable.PSBase.Keys |Where-Object { $userLastLogonTable[$_].LastLogonDate -le (Get-Date).AddDays(-30) }

$staleUserNames now contain the user names of all user accounts that have not logged in for 30 days of more.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1