Search code examples
c#active-directoryuserprincipal

UserPrincipal AccountLockoutTime always null


I'm accessing various properties for user objects in Active Directory. I have the below method which I wrote.

It which works for all the properties except for AccountLockoutTime which always comes back as null.

public IEnumerable<ActiveDirectoryAccount> GetUserAccounts(string samAccountName)
{
    PrincipalContext pricipalContext = new PrincipalContext(ContextType.Domain, "domainname.co.za:3268");
    UserPrincipal userPrincipal = new UserPrincipal(pricipalContext);

    userPrincipal.SamAccountName = "*" + samAccountName + "*";

    PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal);

    ICollection<ActiveDirectoryAccount> result = new List<ActiveDirectoryAccount>();

    foreach (UserPrincipal userSearchResult in principalSearcher.FindAll())
    {
        ActiveDirectoryAccount account = new ActiveDirectoryAccount()
        {
            AccountLockedOut = userSearchResult.IsAccountLockedOut(),
            DistinguishedName = userSearchResult.DistinguishedName,
            Description = userSearchResult.Description,
            Enabled = userSearchResult.Enabled,
            GUID = userSearchResult.Guid,
            LastLogon = userSearchResult.LastLogon,
            LastPasswordSet = userSearchResult.LastPasswordSet,
            // The below line always comes back as null
            LockoutTime = userSearchResult.AccountLockoutTime,
            PasswordNeverExpires = userSearchResult.PasswordNeverExpires,
            SAMAccountName = userSearchResult.SamAccountName,
            SmartcardLogonRequired = userSearchResult.SmartcardLogonRequired,
            UserCannotChangePassword = userSearchResult.UserCannotChangePassword,
            UserPrincipalName = userSearchResult.UserPrincipalName
        };

        if (userSearchResult.GetUnderlyingObjectType() == typeof(DirectoryEntry))
        {
            using (DirectoryEntry entry = (DirectoryEntry)userSearchResult.GetUnderlyingObject())
            {
                account.WhenChanged = (DateTime)entry.Properties["whenChanged"].Value;
                account.WhenCreated = (DateTime)entry.Properties["whenCreated"].Value;

                // Tried the below to get the data as well but no luck. 
                if (userSearchResult.IsAccountLockedOut())
                {
                    if (entry.Properties["lockoutTime"].Value != null)
                    {
                        account.Test = (string)entry.Properties["lockoutTime"].Value;
                    }
                }
            }
        }

        result.Add(account);
    }

    principalSearcher.Dispose();
    return result.ToList();
}

I have locked an account out to check if the code above can read IsAccountLockedOut. It can and returns true. It always returns null for userSearchResult.AccountLockoutTime or (string)entry.Properties["lockoutTime"].Value;

I have checked the lockoutTime property in Active Directory and it is populated for the user account when I lock the account out.

Any ideas as to what is going wrong?

Thanks in advance. :)

Chris


Solution

  • When obtained through entry.Properties["lockoutTime"].Value, the lockoutTime attribute is a COM object that support IADsLargeInteger interface.

    You may use the code here to get its value:

    [ComImport,
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
    Guid("9068270B-0939-11D1-8BE1-00C04FD8D503")]
    public interface IADsLargeInteger
    {
        int HighPart{get;set;}
        int LowPart{get;set;}
    }
    
    private DateTime? GetLockoutTime(DirectoryEntry de)
    {
        DateTime? ret = null;
    
        IADsLargeInteger largeInt = de.Properties["lockoutTime"].Value as IADsLargeInteger;
    
        if (largeInt != null)
        {
            long ticks = (long)largeInt.HighPart << 32 | largeInt.LowPart;
    
            // 0 means not lockout
            if (ticks != 0)
            {
                ret = DateTime.FromFileTimeUtc(ticks.Value);
            }
        }
    
        return ret;
    }
    

    Please note that the value of lockoutTime is the time the account was locked out, but not the "locked until" time.