Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get closest Domain Controller in current AD site without hard coding information

For instances when Active Directory takes too long to replicate data between sites, I need to ensure that the local AD replica contains the most up to date information.

  • How can I get a list of DomainControllers for the current site?

I haven't found anything on Codeproject or on StackOverflow

like image 241
makerofthings7 Avatar asked Jun 12 '12 15:06

makerofthings7


3 Answers

Going to all this trouble is probably wasted effort. Unless you are experiencing issues with the built in logic for finding a domain controller you should just go with the built in method that returns one. According to Microsoft it automatically tries to find the closes one: http://technet.microsoft.com/en-us/library/cc978016.aspx.

Just use the static DomainController.FindOne method and pass in your directorycontext.

Update
Alright, try the code below, let me know how it works for you. It pings each, returns the roundtrip time, if -1 (no connection) it skips it. Flags PDC status if present. Orders by PDC status, followed by ping round trip.

    static void Main(string[] args)
    {
        var dcsInOrder = (from DomainController c in Domain.GetCurrentDomain().DomainControllers
                          let responseTime = Pinger(c.Name)
                          where responseTime >=0
                          let pdcStatus = c.Roles.Contains(ActiveDirectoryRole.PdcRole)
                          orderby pdcStatus, responseTime
                          select new {DC = c, ResponseTime = responseTime} 
                          ).ToList();

        foreach (var dc in dcsInOrder)
        {
            System.Console.WriteLine(dc.DC.Name + " - " + dc.ResponseTime);
        }

        System.Console.ReadLine();
    }

    private static int Pinger(string address)
    {
        Ping p = new Ping();
        try
        {
            PingReply reply = p.Send(address, 3000);
            if (reply.Status == IPStatus.Success) return (int)reply.RoundtripTime;
        }
        catch { }

        return -1;

    }
like image 108
Peter Avatar answered Nov 14 '22 22:11

Peter


First, I'll answer the question that you actually asked:

System.DirectoryServices.ActiveDirectory.ActiveDirectorySite.GetComputerSite().Servers

But it seems like you're asking how to make sure that you're talking to the closest domain controller possible. Windows doesn't exactly provide this functionality, the best it will do is give you a domain controller in the same site that the code is running from.

I think the first thing to check is that you have your sites and subnets configured correctly. Run Active Directory Sites and Services, and make sure that subnets and domain controllers are assigned to the correct sites.

This MSDN page (and the Technet article in Peter's answer) says that you must be searching by the DNS name for the DC Locator to attempt to find a DC in the current site. I don't know if the Name property of the Domain class is the DNS domain name.

I have to assume that DomainController.FindOne is a wrapper for DsGetDcName. At that link, you can find how to turn on tracing for that function. You can use this if you still have problems, or maybe you should just PInvoke this function.

like image 30
Sean Hall Avatar answered Nov 14 '22 22:11

Sean Hall


Here is a code sample that has no hard coding of DCs. Comments and criticism are welcome.

    /// <summary>
    /// For best results ensure all hosts are pingable, and turned on.  
    /// </summary>
    /// <returns>An ordered list of DCs with the PDCE first</returns>
    static LinkedList<DomainController> GetNearbyDCs()
    {
        LinkedList<DomainController> preferredDCs = new LinkedList<DomainController>();
        List<string> TestedDCs = new List<string>();

        using (var mysite = ActiveDirectorySite.GetComputerSite())
        {
            using (var currentDomain = Domain.GetCurrentDomain())
            {
                DirectoryContext dctx = new DirectoryContext(DirectoryContextType.Domain, currentDomain.Name);
                var listOfDCs = DomainController.FindAll(dctx, mysite.Name);

                foreach (DomainController item in listOfDCs)
                {
                    Console.WriteLine(item.Name );
                    if (IsConnected(item.IPAddress))
                    {
                        // Enumerating "Roles" will cause the object to bind to the server
                        ActiveDirectoryRoleCollection rollColl = item.Roles;
                        if (rollColl.Count > 0)
                        {
                            foreach (ActiveDirectoryRole roleItem in rollColl)
                            {
                                if (!TestedDCs.Contains(item.Name))
                                {
                                    TestedDCs.Add(item.Name);
                                    if (roleItem == ActiveDirectoryRole.PdcRole)
                                    {
                                        preferredDCs.AddFirst(item);
                                        break;
                                    }
                                    else
                                    {

                                        if (preferredDCs.Count > 0)
                                        {
                                            var tmp = preferredDCs.First;
                                            preferredDCs.AddBefore(tmp, item);
                                        }
                                        else
                                        {
                                            preferredDCs.AddFirst(item);
                                        }
                                        break;
                                    }
                                } 

                            }
                        }
                        else
                        {
                            // The DC exists but has no roles
                            TestedDCs.Add(item.Name);
                            if (preferredDCs.Count > 0)
                            {
                                var tmp = preferredDCs.First;
                                preferredDCs.AddBefore(tmp, item);
                            }
                            else
                            {
                                preferredDCs.AddFirst(item);
                            }
                        }
                    }
                    else
                    {
                        preferredDCs.AddLast(item);
                    }
                }
            }
        }
        return preferredDCs;
    }
    static bool IsConnected(string hostToPing)
    {
        string pingurl = string.Format("{0}", hostToPing);
        string host = pingurl;
        bool result = false;
        Ping p = new Ping();
        try
        {
            PingReply reply = p.Send(host, 3000);
            if (reply.Status == IPStatus.Success)
                return true;
        }
        catch { }
        return result;
    }
like image 43
makerofthings7 Avatar answered Nov 14 '22 23:11

makerofthings7