Using DsAddressToSiteNamesEx in C# sample: How to get the site and subnet names from an IP Address of a machine

The DsAddressToSiteNamesEx function obtains the site and subnet names corresponding to the addresses specified.

Sample usage: How to get the site and subnet names from an IP Address of a machine

[DllImport("Netapi32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "DsAddressToSiteNamesEx", CharSet = CharSet.Unicode)]
        private static extern int DsAddressToSiteNamesEx(
            [In] string computerName,
            [In] Int32 EntryCount,
            [In] SOCKET_ADDRESS[] SocketAddresses,
            [Out] out IntPtr SiteNames,
            [Out] out IntPtr SubnetNames);

[DllImport("Netapi32.dll")]
private static extern int NetApiBufferFree([In] IntPtr buffer);

private const int ERROR_SUCCESS = 0;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct SockAddr_In
{
    public Int16 sin_family;
    public Int16 sin_port;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] sin_addr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] sin_zero;
}

[StructLayout(LayoutKind.Sequential)]
internal struct SOCKET_ADDRESS
{
    public IntPtr lpSockaddr;
    public Int32  iSockaddrLength;
}

static void Main(string[] args)
{
	string sIP = "10.251.50.101";//@ip = 10.251.50.101
	string sDC = "dc-fr-001";//the name of a domain controller
	IntPtr pSiteNames;
	IntPtr pSubnetNames;
	SOCKET_ADDRESS[] SocketAddresses = new SOCKET_ADDRESS[1];

	String[] ipArray = sIP.Split('.');

	SockAddr_In sockAddr;
	sockAddr.sin_family = 2;
	sockAddr.sin_port = 0;
	sockAddr.sin_addr = new byte[4] { byte.Parse(ipArray[0]), byte.Parse(ipArray[1]), byte.Parse(ipArray[2]), byte.Parse(ipArray[3]) };
	sockAddr.sin_zero = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };

	SocketAddresses[0].iSockaddrLength = Marshal.SizeOf(sockAddr);

	IntPtr pSockAddr = Marshal.AllocHGlobal(16);
	Marshal.StructureToPtr(sockAddr, pSockAddr, true);
	SocketAddresses[0].lpSockaddr = pSockAddr;

	if (DsAddressToSiteNamesEx(
			sDC,
			1,//EntryCount
			SocketAddresses,
			out pSiteNames,
			out pSubnetNames) == ERROR_SUCCESS)
	{
		string sSite = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(pSiteNames, 0));
		string sSubnet = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(pSubnetNames, 0));

		Console.WriteLine("Found site: " + sSite);
		Console.WriteLine("Found subnet: " + sSubnet);
		NetApiBufferFree(pSubnetNames);
		NetApiBufferFree(pSiteNames);
	}
}

Using DsGetSiteName in C# sample: how to get the name of the site where a computer resides?

The DsGetSiteName function returns the name of the site where a computer resides. The site in which the computer resides (as reported by a domain controller) is stored in the computer registry in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ Netlogon\Parameters.

The client IP address is used by the domain controller to retrieve the corresponding subnet that it matches in the Subnets container.
Then the name of the site is retrived from the subnet-to-site mapping.

If the client’s IP address does not match a subnet range of any of the subnets stored in Active Directory, the dclocator will randomly pick a site to use.

Sample usage to get the name of the site where a computer resides:

[DllImport("netapi32.dll", CharSet = CharSet.Auto)]
private static extern int DsGetSiteName(string ComputerName, out IntPtr SiteName);

[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int NetApiBufferFree(IntPtr dwBuffer);

private const int ERROR_SUCCESS = 0;

static void Main(string[] args)
{
	IntPtr pSiteInfo;
	String sSiteName;

	if (DsGetSiteName(
			string.Empty,//ComputerName
			out pSiteInfo) == ERROR_SUCCESS)
	{
		sSiteName = Marshal.PtrToStringAuto(pSiteInfo);
		NetApiBufferFree(pSiteInfo);
		Console.WriteLine("Found workstaton's site Name: " + sSiteName);
	}
}

You can also use a command-line interface for the same purpose:

nltest /server:<HostName> /DsGetSite

How to get group members of Active Directory (Powershell script)

This powershell script allow administrator to get group members of Active Directory. It allow to show all members that is user and computers in group.

Function Get-GroupMembers
{
 <#            
	.DESCRIPTION
		Return the members of an an AD group.
	.PARAMETER GroupDN
		The distinguished name of the group to get membership from.
	.EXAMPLE
		Get-GroupMembers -GroupDN "CN=Managers,OU=Groups,DC=fr,DC=contoso,DC=com" | Format-Table -Property cn, objectSID, distinguishedName
 #>
 Param
	(
 $GroupDN = "CN=Managers,OU=Groups,DC=fr,DC=contoso,DC=com"
	)
 Begin
	{
		$Group = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($GroupDN)")
		$UserAccounts = @()
	}
 Process
	{
		$Members = $Group.member
		
		If ($Members -ne $Null)
		{
			foreach ($User in $Members)
			{
				$UserObject = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($User)")
				If ($UserObject.objectCategory.Value.Contains("Group"))
				{
				}
				Else
				{
					$CurrentUser = New-Object -TypeName PSObject -Property @{
						name = $UserObject.name
						cn = $UserObject.cn
						distinguishedName = $UserObject.distinguishedName
						nTSecurityDescriptor = $UserObject.nTSecurityDescriptor
						objectSID = $UserObject.objectSID
					}
				}
			$UserAccounts += $CurrentUser
			}
		}
	}
 End
	{
		Return $UserAccounts
	}
}

Global catalog and User Logon

A domain controller can locate only the objects in its domain. Locating an object in a different domain would require access to a global catalog server.
A global catalog server is a domain controller that, in addition to its full, writable domain directory partition replica, also stores a partial, read-only replica of all other domain directory partitions in the forest.
The attributes that are replicated to the global catalog are identified in the schema as the partial attribute set (PAS) and are defined by default by Microsoft.
In addition to its activities as a domain controller, the global catalog server supports the following special activities in the forest:
User logon: Domain controllers must contact a global catalog server to retrieve any SIDs of universal groups that the user is a member of.

userlogon

(1) Client logs on to the domain, which prompts
(2) a DNS query for the closest domain controllers.
(3) Client contacts the returned domain controller DCx for authentication.
(4) DCx queries DNS to find the closest global catalog server and then
(5) contacts the returned global catalog server DCy to retrieve the universal groups for the user
The global catalog stores the membership (the member attribute) of only universal groups.

Additionally, if the user specifies a logon name in the form of a UPN (which has the format sAMAccountName@DNSDomainName), the domain controller contacts a global catalog server to retrieve the domain of the user:

userlogonwithUPN

Universal and global group caching and updates: In sites where Universal Group Membership Caching is enabled, domain controllers cache group memberships and keep the cache updated by contacting a global catalog server.
Caching group membership reduces WAN traffic, which helps in sites where updating the cached group membership of security principals generates less traffic than replicating the global catalog to the site.

Using DsGetDcName in C# sample: How to get a Global Catalog

The directory service functions provide a utility for locating a domain controller (DC) in a Windows domain.
The DsGetDcName function (implemented by the Netlogon service) returns the name of a domain controller in a specified domain

Sample usage:

How to get a Global Catalog

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DomainControllerInfo
{
	public string DomainControllerName;
	public string DomainControllerAddress;
	public int DomainControllerAddressType;
	public Guid DomainGuid;
	public string DomainName;
	public string DnsForestName;
	public int Flags;
	public string DcSiteName;
	public string ClientSiteName;
}

[Flags]
private enum DSGETDCNAME_FLAGS : uint
{
	DS_FORCE_REDISCOVERY = 0x00000001,
	DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010,
	DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020,
	DS_GC_SERVER_REQUIRED = 0x00000040,
	DS_PDC_REQUIRED = 0x00000080,
	DS_BACKGROUND_ONLY = 0x00000100,
	DS_IP_REQUIRED = 0x00000200,
	DS_KDC_REQUIRED = 0x00000400,
	DS_TIMESERV_REQUIRED = 0x00000800,
	DS_WRITABLE_REQUIRED = 0x00001000,
	DS_GOOD_TIMESERV_PREFERRED = 0x00002000,
	DS_AVOID_SELF = 0x00004000,
	DS_ONLY_LDAP_NEEDED = 0x00008000,
	DS_IS_FLAT_NAME = 0x00010000,
	DS_IS_DNS_NAME = 0x00020000,
	DS_RETURN_DNS_NAME = 0x40000000,
	DS_RETURN_FLAT_NAME = 0x80000000
}

[DllImport("Netapi32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "DsGetDcNameW", CharSet = CharSet.Unicode)]
private static extern int DsGetDcName(
	[In] string computerName,
	[In] string domainName,
	[In] IntPtr domainGuid,
	[In] string siteName,
	[In] DSGETDCNAME_FLAGS flags,
	[Out] out IntPtr domainControllerInfo);

[DllImport("Netapi32.dll")]
private static extern int NetApiBufferFree(
	[In] IntPtr buffer);

private const int ERROR_SUCCESS = 0;

static void Main(string[] args)
{
	IntPtr pDomainInfo;
	if (DsGetDcName(
			string.Empty,//ComputerName
			"contoso.com",//DomainName
			IntPtr.Zero,//DomainGuid
			string.Empty,//SiteName
			DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED |
			DSGETDCNAME_FLAGS.DS_GC_SERVER_REQUIRED |
			DSGETDCNAME_FLAGS.DS_IS_DNS_NAME |
			DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME,
			out pDomainInfo) == ERROR_SUCCESS)
	{
		DomainControllerInfo dci = new DomainControllerInfo();
		dci = (DomainControllerInfo)Marshal.PtrToStructure(pDomainInfo, typeof(DomainControllerInfo));
		NetApiBufferFree(pDomainInfo);
		pDomainInfo = IntPtr.Zero;
		Console.WriteLine("Found Global catalog DC: " + dci.DomainControllerName);
	}
}