1

I need to export some properties of all PCs of my domain to *.csv table. One of the necessary properties is lastLogOn. The problem is that i have two Domain Controllers, so I need to choose the latest lastLogOn from them.

I have one solution, but it takes really a lot of time (about ~ 1 min) to give me a final array of computers. Here it is:

function getComputers([string]$dc) {
    return Get-ADComputer -SearchBase ‘DC=mydomain,DC=com’ -Server $dc -Filter * `
    -Properties name, samAccountName, DistinguishedName, lastLogOn, OperatingSystem | `
    Sort samAccountName
}
function getComputersFromsBothDCs {
    $compsDC1 = getComputers 'dc1'
    $compsDC2 = getComputers 'dc2'
    $comps = @()
    for ($i = 0; $i -le $compsDC1.Length - 1; $i++) {
        $comp1 = $compsDC1[$i]
        $comp2 = $compsDC2[$i]
        if ($comp1.lastLogOn -ge $comp2.lastLogOn) {
            $comps += $comp1
        } else {
            $comps += $comp2
        }
    }
    return $comps
}

$comps = getComputersFromsBothDCs
# Then export and some other stuff

Function getComputers takes about 1 second per 1 DC, main problem is in choosing the PC with latest lastLogon.

Are there any faster solutions?

  • Try to [avoid using the increase assignment operator (`+=`) to create a collection](https://stackoverflow.com/a/60708579/1701026), see also: [PowerShell scripting performance considerations](https://learn.microsoft.com/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations) – iRon Dec 22 '21 at 09:50
  • Also (`Sort samAccountName`) and doing a side-by-side join is quiet iffy as you might just have created a computer on one DC that isn't yet replicated to other. Without the `Sort` and using this [`Join-Object script`](https://www.powershellgallery.com/packages/Join)/[`Join-Object Module`](https://www.powershellgallery.com/packages/JoinModule) (see also: [In Powershell, what's the best way to join two tables into one?](https://stackoverflow.com/a/45483110/1701026)), you might do something like: `$compsDC1 |Join $compsDC2 -on samAccountName -where { $Left.lastLogOn -ge $Right.lastLogOn }` – iRon Dec 22 '21 at 10:02

2 Answers2

0

Give this a try, should run faster. To consider, this script will only find computers that have the lastLogon property set, it will also add a new property (LastLogonDate) which is the conversion of lastLogon FromFileTime.

$AllDCs = Get-ADDomainController -Filter * # Get all DCs in the Domain
$logons = @{}

$params = @{
    LDAPFilter = '(LastLogon=*)' # Only find computers that have this Property
    SearchBase = 'DC=mydomain,DC=com'
    Properties = @(
        'Name'
        'samAccountName'
        'DistinguishedName'
        'lastLogon'
        'OperatingSystem'
    )
}

foreach($DC in $AllDCs) {
    $params.Server = $DC
    # Find all computers using this target DC

    foreach($computer in Get-ADComputer @params) {
        if($logons[$computer.samAccountName].lastLogon -lt $computer.lastLogon) {
            # On first loop iteration should be always entering this
            # condition:
            # $null -lt $true # => True
            # Assuming $computer.LastLogon is always a populated attribute
            # which also explains my LDAPFilter = '(LastLogon=*)' from before

            $logons[$computer.samAccountName] = $computer | Select-Object @(
                $params.Properties
                @{ N = 'LastLogonDate'; E = { [datetime]::FromFileTime($_.LastLogon) }}
            )
        }
    }
}

$logons.Values # => Is your export
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 1
    Perhaps this could be more efficient with a first pass querying `lastlogonTimeStamp`. Then for any with values less than 14 days, query each domain controller for each computer account and do the arithmetic. It'd be interesting to compare the execution time between the methods. I'm not saying this method is bad at all, just wondering if it could be a little more efficient. It'd depend on whether there's a lot of stale computer accounts, I expect. – LeeM Dec 22 '21 at 14:32
  • 1
    @LeeM I would not use `lastlogon` for this, just following OP's code. The thing is, iirc `lastLogonTimeStamp` and I believe `lastLogon` too don't replicate across domain controllers, the filter would have to be less strict to also find computers that couldn't find before on the other DCs. But I do agree, It would improve the runtime. – Santiago Squarzon Dec 22 '21 at 14:40
  • `LastLogonTimeStamp` *is* in fact replicated - that's why it's up to 14 days behind (`LastLogon` is the most recent per-DC property that isn't replicated). It's also aliased as `LastLogonDate` if you're using the AD Powershell module, so I'd suggest you don't use same alias in your code that's translating `LastLogon` - I got confused by that at first. The AD-PS `LastLogonDate` is `LastLogonTimeStamp` as a `[datetime]` property. https://social.technet.microsoft.com/wiki/contents/articles/22461.understanding-the-ad-account-attributes-lastlogon-lastlogontimestamp-and-lastlogondate.aspx – LeeM Dec 30 '21 at 05:45
0

we can follow the following method to obtain the computer with latest Lastlogon value

(1)- Get the list of ADComputers from both DCs and save the result in a single variable

$DCs = ("DC01,"Dc02")
$ComputersList = @()

foreach ($DC in $DCs){
    $ComputersList += Get-ADComputer -filter {Enabled -eq $true } -Properties lastlogon,OperatingSystem -Server $DC | where {$_.lastlogon -ne $null} | Sort-Object -Property SamAccountName
}

(2) - Group the objects by SamAccountName attribute and sort the grouped result per "lastlogon" attribute then selecting the latest value only

$LatestList = $ComputersList | Group-Object -Property SamAccountName | % {$_.group | Sort-Object -Property lastlogon | select -Last 1}
Mahmoud Moawad
  • 697
  • 7
  • 14