In a recent article, I explained how to configure a Group Policy that allows you to use PowerShell scripts. This means you can take advantage how everything PowerShell can do and apply it to a user logon or logoff script as well as computer startup and shutdown scripts. Let me give you a practical example that demonstrates how to track user logons and logoffs with a PowerShell script.
Avatar

A question I hear often is how to track what computer a user has logged on to. Usually the implication is that the Windows administrator wants a central source that she can easily check. Well what is more central than Active Directory? Since the user can already write to a number of properties on their own user object, why not capture logon (and logoff data as long as we’re at it) and store it with the user?

The script

I wrote a short script that uses ADSI to accomplish this task. I chose this route to avoid requiring that the user’s desktop have any other modules or requirements. Here is my Set-UserStatus.ps1 script.

<#
Set-UserStatus.ps1
****************************************************************
* DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
* THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF    *
* YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
* DO NOT USE IT OUTSIDE OF A SECURE, TEST ENVIRONMENT.         *
****************************************************************
#>

Param(
[Parameter(Position=0)]
[ValidateSet("Logon","Logoff","Unknown")]
[string]$Status="Unknown"
)

#no spaces in the filter
[adsisearcher]$searcher="samaccountname=$env:username"
#find the current user
$find = $searcher.FindOne()
#get the user object
[adsi]$user = $find.Path
#define a string to indicate status
$note = "{0} {1} to {2}" -f (Get-Date),$status.ToUpper(),$env:computername
#update the Info user property
$user.Info=$note
#commit the change
$user.SetInfo()

The script needs a single parameter to indicate Logon or Logoff. The default is Unknown. Because this will be running as Group Policy script, I didn’t want to worry about errors or prompts if the administrator set it up wrong. They would find that out as soon as they tested it, checked the user account and saw “Unknown” for the status.

The script uses ADSI to find the user’s account in Active Directory. It then writes a string with the date and time, the status (ie Logon or Logoff) and the computername. The string is written to the Info property, which is what you see as the Notes property on the Telephones tab.

Telephones tab

Telephones tab

In my test domain, user accounts have permission to write to this property. You might want to pick a different property, perhaps one that doesn’t even show in the GUI. You’ll need to take steps to ensure users have permissions to write to that property on their own object.

Setting it up

To use, I followed the steps in my previous article to copy the script to a GPO and then added it. Because my script takes a parameter, I need to add it as I shown below.

Add PowerShell script to Logon scripts

Add PowerShell script to Logon scripts

If your script uses parameters, I think the best approach is to make them all positional in your script. The screenshots above depicts configuring the script for user logon. After finishing, I then double-clicked Logoff in the same GPO. I clicked Add and then Browse.

Now, I could have copied the script again to the Logoff GPO container, but since the script is the same, in the Explorer window I can click on Scripts in the path to navigate “up”

Scripts folder

Scripts folder

Then double-click the Logon folder.

Navigate to Logon scripts

Navigate to Logon scripts

I can now pick the same script I used for logon.

Add to Logoff scripts

Add to Logoff scripts

Of course, I need to make sure I set the parameter to Logoff.

Set parameter to Logoff

Set the parameter to Logoff

When finished, I can configure any security filtering, link to the necessary organizational units, or the domain and I’m ready to go.

Results

When the user logs on or off, their user account will be updated as I showed in the first screenshot. Depending on your configuration and network, the status update might take a minute or two to show up in Active Directory. My script won’t give you an exact, to the second, time they logged on or off but a pretty close approximation. You might also need to take into account the effect this script might have on replication, so please test thoroughly in a non-production environment.

Of course, with PowerShell this is also easy to discover, and if you use a property that isn’t exposed in Active Directory Users and Computers, you will have no choice but to use PowerShell to find the status information.

Get user status with PowerShell

Get user status with PowerShell

Or I can track a number of users.

PS C:\> Get-ADUser –filter "department –eq 'Customer Service'" –properties Info | Select Name,Info | Out-Gridview –title "CS Users"

Track multiple users

Track multiple users

Summary

So there you go, a practical example of a PowerShell script for users. Now, in this particular case I suppose I could have written something similar using VBScript but this was much easier to do in PowerShell and if that is your management and scripting engine, why wouldn’t you use it!

12 Comments
  1. Avatar
    Christopher Owens 9 years ago

    Interesting way of doing it, I’ll have to think about this one some more…if you use a field that is there in ADUC you could possibly present the data in a column to access it without even opening the user’s properties. This is my way of tracking the data:
    http://community.spiceworks.com/scripts/show/70-track-login-and-logout
    It’s been working well for several years, and I use it at least once an hour!

  2. Avatar
    Kfir 8 years ago

    great script!
    is it possible to put seperators between the time | date | logon | computer name
    and export it to csv?

  3. Avatar Author

    Sure. Look at help for Export-CSV and you’ll see that you can specify a delimiter.

  4. Avatar
    TALAL 7 years ago

    The script is not working in my ad.. May I get help please

    • Avatar Author
      Jeff Hicks 7 years ago

      It is very difficult to troubleshoot via comments but maybe we can get you at least headed in the right path. Of course, you are going to have to provide some information about what isn’t working.

  5. Avatar
    praveen 7 years ago

    i am looking for powershell script to get the user login information from the Windows 2012 for the last 10 Days.

    Kindly help if any one have this.

  6. Avatar
    Andrei Sandulache 7 years ago

    Hi Jeff, nice post, still applicable today 🙂

    Is the string overwritten every time at login or a new line is inserted into the attribute?

    • Avatar
      Andrei Sandulache 7 years ago

      Following up on this, for whoever is interested in writing to a multi-value attribute.

      You need to add PutEx; so based on Jeff’s example:

      Param(
      [Parameter(Position=0)]
      [ValidateSet("Logon","Logoff","Unknown")]
      [string]$Status="Unknown"
      )
      
      #no spaces in the filter
      [adsisearcher]$searcher="samaccountname=$env:username"
      #find the current user
      $find = $searcher.FindOne()
      #get the user object
      [adsi]$user = $find.Path
      #define a string to indicate status
      $note= @("{0} {1} to {2}" -f (Get-Date),$status.ToUpper(),$env:computername)
      #update the attribute
      $user.PutEx(3, 'OtherMobile', $note)
      #commit the change
      $user.SetInfo()

  7. Avatar
    Adam Bergsbaken 7 years ago

    I had the same question, does it over write each time?

    • Avatar
      Andrei Sandulache 7 years ago

      Yes Adam, it will overwrite. See the examples I’ve put in the comments for extended use.

  8. Avatar
    Andrei Sandulache 7 years ago

    Extra follow-up on this.

    Based on the code from Jeff, we expanded the code to support:

    a multivalued attribute (using otherMobile, just replace with what you need)
    limit appending to 50 values (configurable) so as not to clog an attribute

    Param(
    [Parameter(Position=0)]
    [ValidateSet("Logon","Logoff","Unknown")]
    [string]$Status="Unknown"
    )
     
    #no spaces in the filter
    [adsisearcher]$searcher="samaccountname=$env:username"
    #find the current user
    $find = $searcher.FindOne()
    #get the user object
    [adsi]$user = $find.Path
    #define a string to indicate status
    $note= @("{0} {1} to {2}" -f (Get-Date),$status.ToUpper(),$env:computername)
    [array]$NewVals = @()
    $NewVals = $note + ($user.GetEx('OtherMobile'))[0..49]
    #DEBUG, output values in ISE
    #$NewVals |  out-host
    #update the user property
    $user.PutEx(2, 'OtherMobile', $NewVals ) 
    #commit the change
    $user.SetInfo()
    #DEBUG, output values in ISE
    #$user.GetEx('OtherMobile')

  9. Avatar
    Shaun Neighbour 6 years ago

    This was very useful. I changed the login script line 24 to ‘$note = $env:computername’ and the logout script line to ‘$note = “NA”‘

    Those allowed me to run a scheduled powershell script to both disable accounts and log off any of those accounts that were currently in use;

    #Set the search OU
    $OU = "OU=Year 11 Computer Science 2017,OU=Controlled Assessment,OU=Students,OU=Users,DC=mydomain,DC=sch,DC=uk"
    
    Import-Module ActiveDirectory
    
    # Get list of users
    $UserList = Get-ADUser -SearchBase $OU -Filter *
    
    foreach ($User in $UserList){
        
        $CurrentUserDetails = Get-ADuser -Identity $User -Properties * # Grab all user properties
        $Computer = $CurrentUserDetails.info # Set Computer variable to value of user info
        Disable-ADAccount -Identity $User # Disable User account
        write-host Account $User has been disabled.
        IF (($Computer -eq 'NA') -or (!$Computer)) # Check whether user has computer name in info field
            {
                write-host User not logged in
            } ELSE
            {
                write-host Testing connection to workstation $Computer
                IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) # Ping the computer
                { 
                    (gwmi win32_operatingsystem -ComputerName $Computer).Win32Shutdown(4) # Force log off the computer
                    write-host $Computer Logged Off 
                } ELSE 
                { 
                write-host workstation already logged off
            }
        }
        $info = "NA" #define a string to indicate status
        Set-ADUser -Identity $User -Replace @{info="$info"} #update the Info user property
        ;
    }

    This is very useful in a school environment where we have timed assessments and Windows login hours does not match up with our lesson times.

    Thank you!

    Shaun

Leave a reply to Christopher Owens Click here to cancel the reply

Please enclose code in pre tags

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

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account