Click here to Skip to main content
Click here to Skip to main content

How to get the REAL lastlogon datetime from Active Directory

, 21 Mar 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
This article describes how to get the real lastlogon datetime from an user from Active Directory and how to use custom Active Directory attributes.

Introduction

This article describes how to get the real last-logon date-time from an user from Active Directory and how to use custom Active Directory attributes.

Background   

The .NET System.DirectoryServices.AccountManagement classes (from Framework 3.5) provide some neat functionality to access active directory users in a rather simple way. Retrieving a user is as simple as this:

using (var adContext = new PrincipalContext(ContextType.Domain, ADDomainName, ADContainer)
{
    username = "ADusername";
    var foundUser = UserPrincipal.FindByIdentity(adContext, username);
    if (foundUser != null)
    {
        // do all sorts of neat stuff with user object
    }
} 

Now you would expect (as any other normal human being) that getting the lastlogon datetime from the Active Directory user is as simple as this from the UserPrincipal object we just created:

var lastlogonDateTime = founduser.LastLogon;  

 But unfortunately it isn't. After some research I found the following: 'Because the lastLogon attribute is not replicated throughout the domain, if our new user has never logged on to domain controller B then domain controller B will have no knowledge of the user’s last logon time.' 

Anyway, anyhow, it doesn't come up with the right last-logon time. It simply doesn't work, how do we solve this? This gave me a severe headache. 

It is there, but it is hidden. You must retrieve the right time by using extension attributes. By extending the Userprincipal class, you can access these attributes. Not only the real lastlogon time, but also different properties like fax number and so on.  

Using the code  

Below is my extended Userprincipal class which you can use instead of UserPrincipal

using System;
using System.DirectoryServices.AccountManagement;
using System.Reflection;

namespace MyNameSpace
{
    /// <summary>
    /// some properties must be retrieved by getting extended properties
    /// this is unfortunately a protected method and only accessible
    /// by using our own derived user from UserPrincipal
    /// </summary>
    [DirectoryRdnPrefix("CN")]
    [DirectoryObjectClass("user")]
    public class UserPrincipalExtended : UserPrincipal
    {
        public UserPrincipalExtended(PrincipalContext context) : base(context) { }

        public UserPrincipalExtended(PrincipalContext 
            context, 
            string samAccountName, 
            string password,
            bool enabled)
            : base(context, samAccountName, password, enabled) { }

        public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
                                                       string identityValue)
        {
            return (UserPrincipalExtended)FindByIdentityWithType(context,
                                                         typeof(UserPrincipalExtended),
                                                         identityValue);
        }

        public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
                                                       IdentityType identityType,
                                                       string identityValue)
        {
            return (UserPrincipalExtended)FindByIdentityWithType(context,
                                                         typeof(UserPrincipalExtended),
                                                         identityType,
                                                         identityValue);
        }

        #region custom attributes
        [DirectoryProperty("RealLastLogon")]
        public DateTime? RealLastLogon
        {
            get
            {
                if (ExtensionGet("LastLogon").Length > 0)
                {
                    var lastLogonDate = ExtensionGet("LastLogon")[0];
                    var lastLogonDateType = lastLogonDate.GetType();

                    var highPart = (Int32)lastLogonDateType.InvokeMember("HighPart", 
                        BindingFlags.GetProperty, null, lastLogonDate, null);
                    var lowPart = (Int32)lastLogonDateType.InvokeMember("LowPart", 
                        BindingFlags.GetProperty | BindingFlags.Public, null, lastLogonDate, null);

                    var longDate = ((Int64)highPart << 32 | (UInt32)lowPart);

                    return longDate > 0 ? (DateTime?) DateTime.FromFileTime(longDate) : null;
                }

                return null;
            }
        }
        #endregion
    }
}

This will give you the proper LastLogon datetime. And you can add properties to retrieve other (custom) attributes for future purposes: 

[DirectoryProperty("HomePage")]
public string HomePage
{
    get
    {
        if (ExtensionGet("HomePage").Length != 1)
            return null;
        return (string)ExtensionGet("HomePage")[0];
    } 
    set { this.ExtensionSet("HomePage", value); }
}  

This is very convenient as the original Userprincipal class only exposes about 10% of the active directory attributes.  

Mind you use the attributes above the class UserPrincipalExtended. Copy the class literally in your project, change only the namespace and everything will work fine.  Good luck! 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

BackyardHackMechanic

Netherlands Netherlands
No Biography provided

Comments and Discussions

 
QuestionNot really working Pinmembermaro00925-Mar-13 13:17 
GeneralLastLogonTimeStamp PinmemberNarfix25-Mar-13 1:13 
GeneralMy vote of 4 PinmemberPrasad Khandekar21-Mar-13 10:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141015.1 | Last Updated 21 Mar 2013
Article Copyright 2013 by BackyardHackMechanic
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid