FMFF
FMFF

Reputation: 1718

Searching for lastLogon attribute of user in multiple domain servers

First of all, please forgive me if I'm not using the correct terminologies. Correct me wherever I'm using the wrong terminology.

The objective is to programmatically retrieve the lastLogon date of a given username.

We have what I believe is a forest; two AD servers like - adserver01.aa.mycompany.com and adserver02.aa.mycompany.com

I connected to these servers from a third machine using Microsoft's ADExplorer to inspect the objects. There I see some users having lastLogon date available in adserver01, but not in adserver02. For example, the value for lastLogon is 0x0 in adserver02 whereas it is a valid date in adserver01 for some users.

The code I've developed so far as a Windows Forms application, works fine if only one AD Server is involved. How do I check both servers and return the non-zero value if available, in either for lastLogon date attribute?

        private static string GetLastActivityDate(string UserName)
    {
        string domainAndUserName = String.Format(@"LDAP://aa.mycompany.com/CN={0},OU=CLIENT_PROD,OU=clients.mycompany.com,DC=aa,DC=mycompany,DC=com", UserName);
        string OUAdminUserName = "abc";
        string OUAdminPassword = "xyz";
        AuthenticationTypes at = AuthenticationTypes.Secure;
        DateTime lastActivityDate;
        string returnvalue;
        long lastLogonDateAsLong;


        using (DirectoryEntry entryUser = new DirectoryEntry(domainAndUserName, OUAdminUserName, OUAdminPassword, at))
        using (DirectorySearcher mysearcher = new DirectorySearcher(entryUser))
            try
            {
                using (SearchResultCollection results = mysearcher.FindAll())
                {
                    if (results.Count >= 1)
                    {
                        DirectoryEntry de = results[0].GetDirectoryEntry();
                        lastLogonDateAsLong = GetInt64(de, "lastLogon");
                        try
                        {
                            if (lastLogonDateAsLong != -1)
                            {
                                lastActivityDate = DateTime.FromFileTime(lastLogonDateAsLong);
                                returnvalue = lastActivityDate.ToString();
                            }
                            else
                            {
                                returnvalue = "-Not available-";
                            }
                        }
                        catch (System.ArgumentOutOfRangeException aore)
                        {
                            returnvalue = "Not available";
                        }
                    }
                    else
                    {
                        returnvalue = string.Empty;
                    }
                }
            }
            catch (System.DirectoryServices.DirectoryServicesCOMException dsce)
            {
                returnvalue = "- Not available -";
            }


        return returnvalue;
    }

Thank you.

EDIT:

 private static Int64 GetInt64(DirectoryEntry entry, string attr)
    {

        DirectorySearcher ds = new DirectorySearcher(
            entry,
            String.Format("({0}=*)", attr),
            new string[] { attr },
            SearchScope.Base
            );

        SearchResult sr = ds.FindOne();

        if (sr != null)
        {
            if (sr.Properties.Contains(attr))
            {
                return (Int64)sr.Properties[attr][0];
            }
        }
        return -1;
    }

Forgot to mention, the AD schema, structure etc, looks exactly alike in the two servers.

Upvotes: 0

Views: 6326

Answers (2)

Maro
Maro

Reputation: 2629

Check this post http://www.codeproject.com/Articles/19181/Find-LastLogon-Across-All-Windows-Domain-Controlle

I had the same issue but but only for one domain, I solved it by using the following code however i'm checking the lastLogin of all users

 public static Dictionary<string, DateTime> UsersLastLogOnDate()
   {
       var lastLogins = new Dictionary<string, DateTime>();
       DomainControllerCollection domains = Domain.GetCurrentDomain().DomainControllers;
       foreach (DomainController controller in domains)
       {
           try
           {
               using (var directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}", controller.Name)))
               {
                   using (var searcher = new DirectorySearcher(directoryEntry))
                   {
                       searcher.PageSize = 1000;
                       searcher.Filter = "(&(objectClass=user)(!objectClass=computer))";
                       searcher.PropertiesToLoad.AddRange(new[] { "distinguishedName", "lastLogon" });
                       foreach (SearchResult searchResult in searcher.FindAll())
                       {
                           if (searchResult.Properties.Contains("lastLogon"))
                           {
                               var lastLogOn = DateTime.FromFileTime((long)searchResult.Properties["lastLogon"][0]);
                               var username = Parser.ParseLdapAttrValue(searchResult.Properties["distinguishedName"][0].ToString());
                               if (lastLogins.ContainsKey(username))
                               {
                                   if (DateTime.Compare(lastLogOn, lastLogins[username]) > 0)
                                   {
                                       lastLogins[username] = lastLogOn;
                                   }
                               }
                               else
                               {
                                   lastLogins.Add(username, lastLogOn);
                               }
                           }
                       }
                   }

               }


           }
           catch (System.Runtime.InteropServices.COMException comException)
           {
               // Domain controller is down or not responding
               Log.DebugFormat("Domain controller {0} is not responding.",controller.Name);
               Log.Error("Error in one of the domain controllers.", comException);
               continue;
           }
       }
       return lastLogins;
   }

On top of the code you can use the following to get all domains in a forest.

Forest currentForest = Forest.GetCurrentForest();  
DomainCollection domains = currentForest.Domains;  
foreach(Domain domain in domains)  
{  
   // check code above  
}  

Upvotes: 3

geoffc
geoffc

Reputation: 4100

There may be a simpler approach? There is actually another attribute lastLogonTimestamp, added with 2003 domain level I think, that tries to keep a single, consistent value across the domain for the last login. Alas, it has a bizarre replication time pattern, and could be up to two weeks out of date.

Upvotes: 1

Related Questions