• Powershell script to get all AD users’ last logon time

    by  • 2011/05/29 • Tech • 25 Comments

    Update:

    I’ve posted an updated version of this script in a new section of my blog. Because all the comments below relate to the original version of the script I’m going to leave that version posted here. You can find the new version of the script here.

    I wanted to see when all users in a client’s domain last logged in, below.

    This script requires the Quest AD Cmdlets, which you can download here. (Check the box agreeing to the terms to see the download options.)

    You can find a break down of the script, explaining how each section works, here.


    <br />
    ##==============================================================================<br />
    ##==============================================================================<br />
    ## SCRIPT.........: Get-AllUserLastLogon<br />
    ## AUTHOR.........: Clint McGuire<br />
    ## EMAIL..........:<br />
    ## VERSION........: 1<br />
    ## DATE...........: 2011_05_26<br />
    ## COPYRIGHT......: 2011, Clint McGuire<br />
    ## LICENSE........:<br />
    ## REQUIREMENTS...: Powershell v2.0, Quest AD Cmdlets<br />
    ## DESCRIPTION....: Gets all enabled users last logon times and exports to CSV file.<br />
    ## NOTES..........:<br />
    ## CUSTOMIZE......:<br />
    ##==============================================================================<br />
    ## REVISED BY.....:<br />
    ## EMAIL..........:<br />
    ## REVISION DATE..:<br />
    ## REVISION NOTES.:<br />
    ##<br />
    ##==============================================================================<br />
    ##==============================================================================<br />
    <span style="color: #0000ff;"><br />
    ##==============================================================================<br />
    ## START<br />
    ##==============================================================================<br />
    $DCs = Get-QADComputer -ComputerRole DomainController<br />
    $LastLogon = @{}<br />
    ForEach ($DC in $DCs) {<br />
    $Users = Get-QADUser -Service $dc.dnshostname -Enabled<br />
    ForEach ($User in $Users)<br />
    {<br />
    If ($User.LastLogon -ne $null)<br />
    {<br />
    $Time = $User.LastLogon | Get-Date -Format u<br />
    }<br />
    Else<br />
    {<br />
    $Time = $User.LastLogon<br />
    }<br />
    $UserName = $User.DisplayName<br />
    If ($LastLogon.ContainsKey($UserName))<br />
    {<br />
    If ($LastLogon.Get_Item($UserName) -le $Time)<br />
    {<br />
    $LastLogon.Set_Item($UserName, $Time)<br />
    }<br />
    }<br />
    Else<br />
    {<br />
    $LastLogon.Add($UserName, $Time)<br />
    }<br />
    }<br />
    }<br />
    $LastLogon.GetEnumerator() | Sort-Object Name |Export-csv $home\AllADUserLastLogon.csv<br />
    ##==============================================================================<br />
    ## End<br />
    ##==============================================================================<br />
    </span>

    About

    Clint McGuire is a Computer Consultant based out of Vancouver Canada. He specializes in VMware and Storage.

    25 Responses to Powershell script to get all AD users’ last logon time

    1. Dale
      2011/07/27 at 10:04 PM

      Cheers for the script – I was looking to see if I was on the right track writing something similar.

      One thing I’d recommend for anyone looking to use this on a much larger scale is to use $dc.dnshostname so you’re going for the FQDN straight up (or append it – whichever). I’m doing my operations on 40k users over 11DCs in a child domain, so it was very slow during testing just using the netbios name. Using FQDN sped it up a LOT.

      • 2011/07/28 at 2:58 PM

        Thanks for the tip Dale. I’ve updated the script.

    2. Vijay Kumar
      2011/08/09 at 3:37 PM

      I am getting a below error while running this script. I am very new to scripting so please help me resolve this.

      Exception calling “ContainsKey” with “1″ argument(s): “Key cannot be null.
      Parameter name: key”
      At E:\lastlogon.ps1:41 char:27
      + If ($LastLogon.ContainsKey <<<< ($UserName))
      + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
      + FullyQualifiedErrorId : DotNetMethodException

      • clint
        2011/08/14 at 10:49 AM

        I haven’t been able to reproduce this, can you post your script?

    3. Rob
      2011/08/26 at 12:21 PM

      I got the same error as Vijay. I removed the commented out lines and instead of :41 it is now :16
      Here is the script. I just copied and pasted yours into a file and ran it so I made no changes other than removing the comments after the initial error.
      TIA,
      Rob

      $DCs = Get-QADComputer -ComputerRole DomainController
      $LastLogon = @{}
      ForEach ($DC in $DCs) {
      $Users = Get-QADUser -Service $dc.dnshostname -Enabled
      ForEach ($User in $Users)
      {
      If ($User.LastLogon -ne $null)
      {
      $Time = $User.LastLogon | Get-Date -Format u
      }
      Else
      {
      $Time = $User.LastLogon
      }
      $UserName = $User.DisplayName
      If ($LastLogon.ContainsKey($UserName))
      {
      If ($LastLogon.Get_Item($UserName) -le $Time)
      {
      $LastLogon.Set_Item($UserName, $Time)
      }
      }
      Else
      {
      $LastLogon.Add($UserName, $Time)
      }
      }
      }
      $LastLogon.GetEnumerator() | Sort-Object Name |Export-csv $homeAllADUserLastLogon.csv

      • clint
        2011/09/06 at 2:53 PM

        Is it possible that you have users without display names? Try changing line 15 from $User.DisplayName to $User.SamAccountName and re-running the script.

    4. RonP
      2011/09/01 at 12:44 PM

      Hi Clint;

      I’m about as green as you can get wrt using Powershell and my scripting is basic a best. If I wanted to scrape or target this script to run against a specific OU and thena specific Global Group within the container, how can I dio that?

      Not that I’m already grateful for this script but anything else you can do to help would be most appreciative.

      Thanks

      • clint
        2011/09/06 at 3:17 PM

        Excellent question. I’m afraid I don’t have a lot of time to test this, but I would suggest trying to compare $User.DN to the CN of the OU (CN=OU,DC=domain,DC=local). I would try adding If ($User.DN -like “CN of OU”) before the first If where LastLogon is checked.
        To check the group status you can use $User.MemberOf and $User.NestedMemberOf, these should also output the CN of the group.

        Email me if you need more help, my address is on the contact page.

        • clint
          2011/09/14 at 9:37 PM

          Hi RonP,

          Sorry it has taken me so long to get back to you. Code below.
          You will need to update the SearchRoot (line 30) with the your domain and OU, and modify the -contains on line 33 to have the proper group DN. The easiest way to find the correct values, I can think of, is to run Get-QADUser for a user you know is in the correct OU and Group, and pipe that to Format-List (Get-QADUser clint |Format-List), then look for the Properties ParentContainer and MemberOf.

          ##==============================================================================
          ##==============================================================================
          ## SCRIPT………: Get-OUandGroupUserLastLogon
          ## AUTHOR………: Clint McGuire
          ## EMAIL……….:
          ## VERSION……..: 1
          ## DATE………..: 2011_09_14
          ## COPYRIGHT……: 2011, Clint McGuire
          ## LICENSE……..:
          ## REQUIREMENTS…: Powershell v2.0, Quest AD Cmdlets
          ## DESCRIPTION….: Gets all enabled users from a specific OU and Group
          ## to find their last logon times and exports to CSV file.
          ## NOTES……….:
          ## CUSTOMIZE……:
          ##==============================================================================
          ## REVISED BY…..:
          ## EMAIL……….:
          ## REVISION DATE..:
          ## REVISION NOTES.:
          ##
          ##==============================================================================
          ##==============================================================================

          ##==============================================================================
          ## START
          ##==============================================================================
          $DCs = get-qadcomputer -ComputerRole DomainController
          $LastLogon = @{}
          ForEach ($DC in $DCs) {
          $Users = Get-QADUser -Service $dc.dnshostname -Enabled -SearchRoot ‘domain.local/OUName’
          ForEach ($User in $Users)
          {
          If ($User.MemberOf -contains ‘cn=group name,cn=users,dc=domain,dc=local’)
          {
          If ($User.LastLogon -ne $null)
          {
          $Time = $User.LastLogon | Get-Date -Format u
          }
          Else
          {
          $Time = $User.LastLogon
          }
          $UserName = $User.DisplayName
          if ($LastLogon.ContainsKey($UserName))
          {
          if ($LastLogon.Get_Item($UserName) -le $Time) {
          $LastLogon.Set_Item($UserName, $Time)
          }
          }
          else{
          $LastLogon.Add($UserName, $Time)
          }
          }
          }
          }
          $LastLogon.GetEnumerator() | Sort-Object Name |export-csv $home\documents\ServiceLastLogon.csv -NoTypeInformation
          ##==============================================================================
          ## End

          ##==============================================================================

    5. Rob
      2011/09/13 at 2:21 PM

      Hi Clint,

      Thanks for help. Here is the script that you sent me to change the the User Display Name to User Sam Account. This fix work.

      Thanks for your help. Rob

      Her’s the fix:

      Is it possible that you have users without display names? Try changing line 15 from $User.DisplayName to $User.SamAccountName and re-running the script.

      I’ve included the modified version below. I noticed there is a \ missing in the output of the Export-Csv at the end, I might just be a wordpress thing, but I’ve included it below.

      $DCs = Get-QADComputer -ComputerRole DomainController
      $LastLogon = @{}
      ForEach ($DC in $DCs) {
      $Users = Get-QADUser -Service $dc.dnshostname -Enabled
      ForEach ($User in $Users)
      {
      If ($User.LastLogon -ne $null)
      {
      $Time = $User.LastLogon | Get-Date -Format u
      }
      Else
      {
      $Time = $User.LastLogon
      }
      $UserName = $User.SamAccountName
      If ($LastLogon.ContainsKey($UserName))
      {
      If ($LastLogon.Get_Item($UserName) -le $Time)
      {
      $LastLogon.Set_Item($UserName, $Time)
      }
      }
      Else
      {
      $LastLogon.Add($UserName, $Time)
      }
      }
      }
      $LastLogon.GetEnumerator() | Sort-Object Name |Export-csv $home\AllADUserLastLogon.csv

    6. Vance McMillan
      2011/10/05 at 8:36 PM

      When I run this script, I get errors – Get-QADComputer isn’t recognised, nor is Get-QADUser.

      I’ve checked and Powershell only requires Get-ADComputer and Get-ADUser, so I’ve tweaked my scripts to use these, but then I get the following error:

      Exception calling “ContainsKey” with “1” argument(s): “Key cannot be null.
      Parameter name: key”
      At C:\scripts\AllADUserLastLogon.ps1:20 char:27
      + If ($LastLogon.ContainsKey <<<< ($UserName))
      + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
      + FullyQualifiedErrorId : DotNetMethodException

      My script has been updated to the following:

      ##==============================================================================
      ## START
      ##==============================================================================
      $DCs = Get-ADComputer -ComputerRole DomainController
      $LastLogon = @{}
      ForEach ($DC in $DCs) {
      $Users = Get-ADUser -Filter *
      ForEach ($User in $Users)
      {
      If ($User.LastLogon -ne $null)
      {
      $Time = $User.LastLogon | Get-Date -Format u
      }
      Else
      {
      $Time = $User.LastLogon
      }
      $UserName = $User.SamAccountName
      If ($LastLogon.ContainsKey($UserName))
      {
      If ($LastLogon.Get_Item($UserName) -le $Time)
      {
      $LastLogon.Set_Item($UserName, $Time)
      }
      }
      Else
      {
      $LastLogon.Add($UserName, $Time)
      }
      }
      }
      $LastLogon.GetEnumerator() | Sort-Object Name |Export-csv $home\AllADUserLastLogon.csv
      ##==============================================================================
      ## End
      ##==============================================================================

      • clint
        2011/10/24 at 11:55 PM

        Hi Vance,

        The original requires Quest’s AD cmdlets, which you can find here: http://www.quest.com/activeroles-server/arms.aspx

        Are you sure that is the script you are running? When I run the version you posted I get this error:
        Get-ADComputer : A parameter cannot be found that matches parameter name ‘ComputerRole’.

        If I remove the -ComputerRole it asks for a filter (I gave it *), then it created the CSV file with a list of usernames but no last logon time (values to go with the keys).

        Perhaps get the Quest cmdlets and use my original?

        Good luck,

    7. Pingback: Powershell script to get all computers last logon time « Clint McGuire

    8. Sam R
      2012/12/21 at 9:31 AM

      This script is great, spent forever looking for something like this. One thing that would be nice is it added the OU to the CSV. I work with a bunch of school districts and it would make tracking down the users much nicer.

      • Clint
        2012/12/23 at 10:10 PM

        Thanks Sam,
        Unfortunately hash tables only support a pair of values, but you could create a list of all users…
        $users =get-qaduser
        $OUTable = @{}
        foreach ($user in $users) {
        $UserName = $User.DisplayName
        $OU = $User.ParentContainerDN
        $OUTable.Add($UserName, $OU)
        }
        $OUTable.GetEnumerator() | Sort-Object Name |Export-csv $home\OUOutput.csv -NoTypeInformation

    9. kevin
      2013/08/08 at 2:08 AM

      can i just copy the script for the ad. and can i specify an container with 6 units with users?

      • Clint
        2013/08/08 at 3:32 PM

        Yes, you can.
        I would suggest starting with my updated version of the script, which is on this page:
        http://www.clintmcguire.com/get-alluserlastlogon/

        • kevin
          2013/08/08 at 10:25 PM

          ok i tried but now i get an error

          get-qadcomputer : The term ‘get-qadcomputer’ is not recognized as the name of a cmdlet, function, script file, or
          operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
          again.
          At line:1 char:8
          + $DCs = get-qadcomputer -ComputerRole DomainController
          + ~~~~~~~~~~~~~~~
          + CategoryInfo : ObjectNotFound: (get-qadcomputer:String) [], CommandNotFoundException
          + FullyQualifiedErrorId : CommandNotFoundException

          • Clint
            2013/08/09 at 12:44 AM

            Have you downloaded the Quest AD Cmdlets?
            Get-QADComputer is a part of that.

            • kevin
              2013/08/09 at 4:55 AM

              no i cant install it right now administrators above me have to do this.

    10. Kevin De Schrijver
      2014/02/14 at 5:29 AM

      Very nice script. Works excellently.

      One small request. Is it at all possible to add more information to the output? For instance I’d like to create a list of all users, their last logon time AND their description.

      Other things that might come in handy are creation date, last modified etc etc. But I assume once I understand how to add more user attributes to the listing it’ll explain itself.

      • Clint
        2014/02/14 at 11:19 AM

        The way the script is built, no.
        I use a hashtable to store the username and the time as a pair.

        It would be possible to replace the hashtable with an array that held all of the info you want, then storing a copy of each of these arrays, per user, into another array (or hashtable).
        You could then dump that to CSV.

        The author of this script uses this approach here:
        http://deptive.co.nz/xenapp-farm-health-check-v2/
        There is a lot of stuff going on, but he is storing a hastable in a hashtable. ($tests getting stored in $allresults)

    11. sajjad haider
      2014/03/12 at 11:26 PM

      How Can I get list of all users from Child Domains with Bidirectional (two-way trust) and , Forest Trust ,

      Quarantined Domain (External) with Bidirectional (two-way trust) – like we have one domain for employees and one domain for developement systems and users- and one child domain for franchise users – how I can get users list from all these three domains – one is child domain and other is trusted domain

      • Clint
        2014/03/13 at 12:41 AM

        Run the script from a computer that is a domain member of each of those domain, while logged in as a user of that domain.
        Or create 3 copies of the script, each pointing to one domain, then run the script as a user with sufficient rights in the target domain.

    12. Kirk Kenton
      2014/11/14 at 9:27 AM

      Great script. Thank you.
      I made a couple slight changes. First I added an ldapfilter to the Get-QADUser to only retrieve accounts of EmployeeType Employee or Contractor and enabled to reduce the data returned for my needs. Then also only retrieve the sAMAccountName and lastLogon attribute. I updated the line setting the $UserName variable from Displayname to sAMAccountName. In my environment Displayname is not always unique.

      Anyway, thanks again.

      Kirk

    Leave a Reply

    Your email address will not be published. Required fields are marked *