I am trying to figure out the correct coding for a Powershell script. I want it to look at all of our Domain Controllers and use the most recent lastlongondate. After it has the data I want it to categorize them via OS so it would say Windows 10 - 100 Server 2008 - 10 ex.... I cant seem to figure out how to get it to check all the controllers for the appropriate data. This is what I have so far. Does anyone see what I am doing wrong?

function ImportADModule
  Import-Module ActiveDirectory
  if (!($?))
    Add-WindowsFeature RSAT-AD-PowerShell
    Import-Module ActiveDirectory

function GetDN
  $names = $domain.Split(".")
  $bFirst = $true
  foreach ($name in $names)
    if ($bFirst)
      $dn += "DC=" + $name
      $bFirst = $false
    else { $dn += ",DC=" + $name }
  return $dn

function GetDNs
  $dns = @{}
  foreach ($domain in $domains)
    $dns.Add($domain, (GetDN -domain $domain))
  return $dns

function GetOSCountsPerDomain
  param($dns, $enabled, $daysOld)
  $osCounts = @{}
  $cutOffDate = ((Get-Date).Adddays(-($daysOld))).ToFileTime()
  Write-Host "Getting Data" -NoNewline -ForegroundColor Yellow

  $filter = "(PwdLastSet -gt {0}) -and (Enabled -eq '{1}')" -f $cutOffDate, $enabled
  foreach ($domain in $dns.GetEnumerator())
    $i = 0
    $domains = @{}
    Write-Host "." -NoNewline -ForegroundColor Yellow
    $computers = Get-ADComputer -Filter $filter -SearchBase $domain.Value -Server $domain.Key -Properties OperatingSystem
    foreach ($computer in $computers)
      if ($computer.OperatingSystem -eq $null) { $os = 'NULL'}
      else { $os = $computer.OperatingSystem }
      try { $domains.Add(($os + " - " + $osver), 1) }
      catch { $domains.Set_Item(($os + " - " + $osver), ($domains.Get_Item($os + " - " + $osver))+1) }
    $osCounts.Add($domain.Key, $domains)
  return $osCounts

function DisplayOutput
  foreach ($osCount in $osCounts.GetEnumerator())
    Write-Host $OSCount.Key -ForegroundColor Green
    $osCount.Value.GetEnumerator() | Sort-Object Value -Descending | Format-Table -AutoSize


#Import AD Module for PowerShell

#Get list of domains from current forest
$Domains = (Get-ADForest).domains

#Get hash table of domains and distinguished names from current forest
$DNs = GetDNs -domains $Domains

#Get OS counts per domain (specify age here)
$OSCounts = GetOSCountsPerDomain -dns $DNs -enabled $true -daysOld 7

#Display Results
$Body = DisplayOutput -osCounts $OSCounts 

#Send email settings
$email = @{
From = "email"
To = "email"
Subject = "Operating System Counts"
SMTPServer = "mail.mail.org"
Body = $body| Out-String

send-mailmessage @email
  • I didn't try your code, but quickly looking at it, you're missing a " at the end of From = "email. Unless that's a copy-paste error.
    – shinjijai
    Oct 16, 2017 at 19:05
  • Your correct I was but that is most definitely not the solution to this problem lol. That was a sanitizing issue.
    – Norrec
    Oct 16, 2017 at 19:10
  • Are you wanting to write your own functions or are you happy using PowerShell cmdlets?
    – Tim Haintz
    Oct 16, 2017 at 19:53

1 Answer 1


The below should do what you are looking for. I haven't added the 'send email' part. Just answered your specific question around the domain controllers and the count of them.

$domaincontrollers = Get-ADDomainController

$dcinfo = foreach($computer in $domaincontrollers)
              Get-Adcomputer -Identity $computer.Name -Properties LastLogonDate,OperatingSystem

$dcinfo | Group-Object OperatingSystem

UPDATE - Re-reading your question, I'm not sure if you want all computers from the Domain Controller with the latest last logon date. If that's the case, to get the Domain Controller with the lastest last logon date and then all computers from that server, you would use the below. Thanks to this link.

$domaincontrollers = Get-ADDomainController
$lastlogondomaincontroller = $domaincontrollers | Foreach-Object {$_.LastLogonDate = $_.LastLogonDate; $_} | `
                             Group-Object Name | `
                             Foreach-Object {$_.Name | Sort-Object LastLogonDate | Select-Object -Last 1}

Get-Adcomputer -Filter * -Server $lastlogondomaincontroller -Properties LastLogonDate,OperatingSystem | Group-Object OperatingSystem   

UPDATE 2 - After comments.

$domaincontrollers = (Get-ADForest).Domains | ForEach-Object { Get- ADDomainController -Filter * -Server $_ }

$allcomputersfromalldcs = foreach($computer in $domaincontrollers)
                              Get-Adcomputer -filter * -server $computer.Name -Properties LastLogonDate,OperatingSystem

$uniquecomputers = $allcomputersfromalldcs | Select-Object Name,OperatingSystem,LastLogonDate | Sort-Object Name,LastLogonDate | Select-Object Name,OperatingSystem,LastLogonDate -Unique

$uniquecomputers | Group-Object OperatingSystem

The Update 2 answer gets all of your DCs and then checks for all computers on each of your DCs. Because there are going to be duplicate machines, $uniquecomputers takes all of the computers, sorts them by name and lastlogondate. I then use -Unique to retrieve only a single machine name.

You can then use Group-Object to get the values. If you want the actual numbers for the email, you could use

$uniquecomputers | Group-Object OperatingSystem | Select-Object Count,OperatingSystem

I have stored things in variables also so that you can use them for your email if required.

Hope this is what you were looking for.

Thanks, Tim.

  • This is much closer to what I am looking for. But the issue is we have several DC's and all the computers don't check into each DC, what I am looking for is to query ALL the DC's for each computer to pull the most recent lastlogondate and use that for the counting purposes. for the last 30 days or 7 days, so ideally the numbers should be different in the returns since we have a lot of contractors who dont log in everyday on their machines.
    – Norrec
    Oct 17, 2017 at 11:20
  • Sorry for the delay. Appears our time zones don’t line up well. I will have a think about the compare and add a third code update in my answer which will hopefully help.
    – Tim Haintz
    Oct 17, 2017 at 18:03
  • No problem. This seems to be much harder than one would of expected it to be.
    – Norrec
    Oct 18, 2017 at 12:20
  • Didn’t get a chance to look at it today sorry. Will be a sort-object operatingsystem, lastlogintime | select -unique. Something like that. Haven’t had a chance to test it yet though and update the answer. Should have a chance in the morning here. Thanks.
    – Tim Haintz
    Oct 18, 2017 at 12:30
  • Replying on mobile phone too so there might be a typo or two in there.
    – Tim Haintz
    Oct 18, 2017 at 12:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.