Get nETBIOSName from a UserPrincipal object
Asked Answered
T

5

12

I am using the System.DirectoryServices.AccountManagement part of the .Net library to interface into ActiveDirectory.

Having called GetMembers() on a GroupPrincipal object and filter the results, I now have a collection of UserPrincipal objects

GroupPrincipal myGroup;  // population of this object omitted here 

foreach (UserPrincipal user in myGroup.GetMembers(false).OfType<UserPrincipal>())
{
    Console.WriteLine(user.SamAccountName);
}

The above code sample will print out usernames like "TestUser1". I need to compare these to a list coming from another application in "DOMAIN\TestUser1" format.

How do I get the "DOMAIN" part from the UserPrincipal object?

I can't just append a known domain name as there are multiple domains involved and I need to differentiate DOMAIN1\TestUser1 and DOMAIN2\TestUser2.

Trellis answered 26/11, 2010 at 10:54 Comment(4)
@marc_s The UserPrincipleName contains the name in [email protected] format - I can't see how to easily convert that into DOMAIN\user format (especially as the domains involved are a known list - each production environment will be a different list of domains than my dev environment)Trellis
You can also use the msDS-PrincipalName property as described here stackoverflow.com/questions/10702188Kummerbund
Or use user.Sid.Translate(typeof(System.Security.Principal.NTAccount)).ToString() to get the Domain\Username of each group member. See stackoverflow.com/questions/6759463Kummerbund
@GregBray msDs-Principalname did not exist in Win2003 for AD LDS systems (see msdn.microsoft.com/en-us/library/cc221208.aspx) and on AD DS may contain the NETBIOS name (see msdn.microsoft.com/en-us/library/cc223404.aspx). I no longer have access to the domains I originally had this problem with to check - but your suggestion may be useful to those that follow...Trellis
B
4

You have two choices that I can think of.

  1. Parse, or take everything that is on, the right of [email protected];
  2. Use the System.DirectoryServices namespace.

I don't know about UserPrincipal, neither do I about GroupPrincipal. On the other hand, I know of a working way to achive to what you want.

[TestCase("LDAP://fully.qualified.domain.name", "TestUser1")] 
public void GetNetBiosName(string ldapUrl, string login)
    string netBiosName = null;
    string foundLogin = null;

    using (DirectoryEntry root = new DirectoryEntry(ldapUrl))
        Using (DirectorySearcher searcher = new DirectorySearcher(root) {
            searcher.SearchScope = SearchScope.Subtree;
            searcher.PropertiesToLoad.Add("sAMAccountName");
            searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", login);

            SearchResult result = null;

            try {
                result = searcher.FindOne();

                if (result == null) 
                    if (string.Equals(login, result.GetDirectoryEntry().Properties("sAMAccountName").Value)) 
                        foundLogin = result.GetDirectoryEntry().Properties("sAMAccountName").Value
            } finally {
                searcher.Dispose();
                root.Dispose();
                if (result != null) result = null;
            }
        }

    if (!string.IsNullOrEmpty(foundLogin)) 
        using (DirectoryEntry root = new DirectoryEntry(ldapUrl.Insert(7, "CN=Partitions,CN=Configuration,DC=").Replace(".", ",DC=")) 
            Using DirectorySearcher searcher = new DirectorySearcher(root)
                searcher.Filter = "nETBIOSName=*";
                searcher.PropertiesToLoad.Add("cn");

                SearchResultCollection results = null;

                try {
                    results = searcher.FindAll();

                    if (results != null && results.Count > 0 && results[0] != null) {
                        ResultPropertyValueCollection values = results[0].Properties("cn");
                        netBiosName = rpvc[0].ToString();
                } finally {
                    searcher.Dispose();
                    root.Dispose();

                    if (results != null) {
                        results.Dispose();
                        results = null;
                    }
                }
            }

    Assert.AreEqual("INTRA\TESTUSER1", string.Concat(netBiosName, "\", foundLogin).ToUpperInvariant())
}

Other related information or links available in this SO question.
C# Active Directory: Get domain name of user?
How to find the NetBIOS name of a domain

Bluetongue answered 26/11, 2010 at 14:35 Comment(8)
Looks good but doesn't work. The result.GetDirectoryEntry() doesn't have a "nETBIOSName" property - it is always null. (Plus I had to use Properties["nETBIOSName"].Value and my result object doesn't have a Dispose() - using .Net4 if that makes a difference)Trellis
@Grmh: I edited my answer. Please take a look at it and tell me whether it works. I have encapsulated it within a NUnit TestCase for ease of testing.Bluetongue
@Will: I don't appear to have a "CN=Partitions,CN=Configuration" in my domain. My domain is apparently a child domain and the parent has a CN=Copnfiguration section. I'll have to investigate....Trellis
If I query the parent domain, it get two results. results[0].Properties["cn"][0] gives the parent domains NetBIOS name, whilst results[1].Properties["cn"][0] gives the child domains NetBIOS name. This gives a list of possible NETBIOS names. I guess I'll have to look for each netbiosname\loginname combo to see if it returns a valid user and that it matches the original user. Does seem kinda tortuous though. :(Trellis
@Grhm: Agreed, it is tortuous. I think the simplest possible way would be to take the right part of the @ sign on the [email protected] and parse concatenate it to form fully.qualified.domain.name\login. Would this work for best for you?Bluetongue
@Will: Unfortunately I think I'll have to. I was hoping to compare a list of users to a list of logon names and report any discrepanices. The list of logons is supplied in DOMIAN\user format and user was expecting output in that format.Trellis
It is no wrong to tell the user to provide with a list of full domain\login due to the limitations or or knowledge. The best thing possible is to stay transparent to the user. =) Hope I helped at least a bit.Bluetongue
@Will: With your help, I now attempt to get the nETBIOSName property from the user, and if not present fallback to user@fqdn format. The first option may work in the customer environment and if not the code will return a sane known format. I'l markj this as answered since there doesn't appear to be a better answer.Trellis
N
2

Use the ActiveDs COM library, it has built-in name translation that works and does not make any assumptions (like other answers here).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ActiveDs;

namespace Foo.Repository.AdUserProfile
{
    public class ADUserProfileValueTranslate
    {
        public static string ConvertUserPrincipalNameToNetBiosName(string userPrincipleName)
        {
            NameTranslate nameTranslate = new NameTranslate();
            nameTranslate.Set((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_USER_PRINCIPAL_NAME, userPrincipleName);
            return nameTranslate.Get((int) ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_NT4);
        }
    }
}
Northeast answered 5/6, 2011 at 14:27 Comment(0)
C
0

You could look for the possible domains in the user.DistinguishedName property. A user in Domain 1 should contain the string "DC=DOMAIN1". It definitely shouldn't contain the string "DC=DOMAIN2".

Caviness answered 20/8, 2012 at 16:21 Comment(2)
Not necessarily. I had a domain that was [email protected] or PROJECTDEV\User1 The user.DistinguishedName ended with DC=dev,DC=project,DC=local it does not contain DC=PROJECTDEVTrellis
Ah - thx for the correction. I had thought distinguished name was in some sense a calculated field - and it's consistent in my environment.Caviness
N
0

As mentioned in one of the comments to the question I think this is a good answer for more recent times:

 user.Sid.Translate(typeof(System.Security.Principal.NTAccount)).ToString()
Northeast answered 27/7, 2017 at 1:24 Comment(0)
C
-1

Have you tried passing the fully qualified domain name to this other app? Most windows API's won't complain if you do fully_qualified_domain\USER.

Compander answered 26/11, 2010 at 14:15 Comment(1)
Unfortunately the other app stores the usernames in a database and expects the username to be unique. It also interfaces to a number of other apps that I dont have access to.Trellis

© 2022 - 2024 — McMap. All rights reserved.