Skip to main content

How to determine Active Directory Domain Policies in C# using DirectorySearcher.

using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;

namespace DotNetDevGuide.DirectoryServices.Chapter10
{
    /// <summary>
    /// Determining Domain Policies
    ///
    /// This code is meant to run on an Active Directory domain.
    /// Where does this leave ADAM instances? By default, ADAM will assume any
    /// local or domain policies on the Windows 2003 server where it is running.
    /// This means that if our Windows 2003 server is a member of the domain, we
    /// can simply use code similar to that in Listing 10.7. If, however, the server
    /// is running in a workgroup configuration, the policy will be determined locally.
    /// Therefore, this code would not be appropriate. Instead, we would need to know
    /// our local policy or attempt to discover it using Windows Management
    /// Instrumentation (WMI) classes.
    ///
    /// Source: Addison Wesley - The .NET Developer’s Guide to Directory Services Programming
    /// Listing 10.7 in full
    /// Released: May 2006, by Publisher(s) Addison-Wesley Professional
    /// ISBN: 0321350170
    /// </summary>
    public class DomainPolicy
    {
        ResultPropertyCollection _attribs;

        public DomainPolicy(DirectoryEntry domainRoot)
        {
            string[] policyAttributes = new string[]
            {
                "maxPwdAge", "minPwdAge", "minPwdLength",
                "lockoutDuration", "lockOutObservationWindow",
                "lockoutThreshold", "pwdProperties",
                "pwdHistoryLength", "objectClass",
                "distinguishedName"
            };

            //we take advantage of the marshaling with
            //the DirectorySearcher for Large Integer values...
            DirectorySearcher ds = new DirectorySearcher(
                domainRoot,
                "(objectClass=domainDNS)",
                policyAttributes,
                SearchScope.Base
            );

            SearchResult result = ds.FindOne();

            //do some quick validation...
            if (result == null)
            {
                throw new ArgumentException("domainRoot is not a domainDNS object.");
            }

            _attribs = result.Properties;
        }

        //for some odd reason, the intervals are all stored
        //as negative numbers.  We use this to "invert" them
        private long GetAbsValue(object longInt)
        {
            return Math.Abs((long)longInt);
        }

        public string DomainDistinguishedName
        {
            get
            {
                string val = "distinguishedName";

                if (_attribs.Contains(val))
                {
                    return (string)_attribs[val][0];
                }

                //default return value
                return String.Empty;
            }
        }

        public TimeSpan MaxPasswordAge
        {
            get
            {
                string val = "maxPwdAge";

                if (_attribs.Contains(val))
                {
                    long ticks = GetAbsValue(_attribs[val][0]);
                    if (ticks > 0)
                    {
                        return TimeSpan.FromTicks(ticks);
                    }
                }

                return TimeSpan.MaxValue;
            }
        }

        public TimeSpan MinPasswordAge
        {
            get
            {
                string val = "minPwdAge";

                if (_attribs.Contains(val))
                {
                    long ticks = GetAbsValue(attribs[val][0]);
                    if (ticks > 0)
                    {
                        return TimeSpan.FromTicks(ticks);
                    }
                }

                return TimeSpan.MinValue;
            }
        }

        public TimeSpan LockoutDuration
        {
            get
            {
                string val = "lockoutDuration";

                if (_attribs.Contains(val))
                {
                    long ticks = GetAbsValue(_attribs[val][0]);
                    if (ticks > 0)
                    {
                        return TimeSpan.FromTicks(ticks);
                    }
                }

                return TimeSpan.MaxValue;
            }
        }

        public TimeSpan LockoutObservationWindow
        {
            get
            {
                string val = "lockoutObservationWindow";

                if (_attribs.Contains(val))
                {
                    long ticks = GetAbsValue(_attribs[val][0]);
                    if (ticks > 0)
                    {
                        return TimeSpan.FromTicks(ticks);
                    }
                }

                return TimeSpan.MaxValue;
            }
        }

        public int LockoutThreshold
        {
            get
            {
                string val = "lockoutThreshold";

                if (_attribs.Contains(val))
                {
                    return (int)_attribs[val][0];
                }

                return 0;
            }
        }

        public int MinPasswordLength
        {
            get
            {
                string val = "minPwdLength";

                if (_attribs.Contains(val))
                {
                    return (int)_attribs[val][0];
                }

                return 0;
            }
        }

        public int PasswordHistoryLength
        {
            get
            {
                string val = "pwdHistoryLength";

                if (_attribs.Contains(val))
                {
                    return (int)_attribs[val][0];
                }

                return 0;
            }
        }

        public PasswordPolicy PasswordProperties
        {
            get
            {
                string val = "pwdProperties";

                // this should fail if not found
                return (PasswordPolicy)_attribs[val][0];
            }
        }
    }
}