is there a way to search the Active Directory using UserPrincipal only having the SID?
I have the SID in byte[] (previously queried with DirectorySearcher) and used a StringBuilder to convert it to "S-1-15-..." and "\01\05..".
I tried to handle it this way:
PrincipalContext pContext = new PrincipalContext(ContextType.Domain);
UserPrincipal pUser = new UserPrincipal(pContext);
pUser.Sid = stringBuilder.ToString();
PrincipalSearcher pSearcher = new PrincipalSearcher();
pSearcher.QueryFilter = pUser;
Console.WriteLine(pSearcher.FindOne().DistinguishedName.ToString());
Visual Studio tells me, that the Sid is write protected. Of course...
Thanks in advance & Cheers
Alex
p.s.: I already tried to solve it the way described here: How can I convert from a SID to an account name in C#, but no success here.
You certainly can. I use the below method to find my users in an internal application called "Atlas". Please excuse the formatting.
using (var context = new PrincipalContext(ContextType.Domain, "DOMAIN_NAME_IMPORTANT"))
{
var userIdentity = UserPrincipal.FindByIdentity(context, "USER GUID GOES HERE"));
}
Better late than never. Your question helped me along, so I thought I would try to add completeness to this answer for posterity. Note that my context was Local Machine, for which FindByIdentity is too slow.
You were on the right trail. I found this answer particularly helpful to use LINQ to specify the search parameters in the collection returned by the PrincipalSearcher. These queries did the trick for me to quickly find the accounts via SID:
private static GroupPrincipal GetGroup(string accountSid)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
GroupPrincipal oGroupPrincipal = new GroupPrincipal(oPrincipalContext);
return new PrincipalSearcher(oGroupPrincipal).FindAll().Cast<GroupPrincipal>()
.FirstOrDefault(x => x.Sid.Value.Equals(accountSid, StringComparison.OrdinalIgnoreCase));
}
private static UserPrincipal GetUser(string accountSid)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = new UserPrincipal(oPrincipalContext);
return new PrincipalSearcher(oUserPrincipal).FindAll().Cast<UserPrincipal>()
.FirstOrDefault(x => x.Sid.Value.Equals(accountSid, StringComparison.OrdinalIgnoreCase));
}
private static PrincipalContext GetPrincipalContext()
{
return new PrincipalContext(ContextType.Machine, Environment.MachineName);
}
For further details, see my post on this subject.
Related
I have this powershell function and i want to make it as a C# function.
How can i put it into C#?
Get-ADComputer -filter {Name -Like 'myComp'} -property * | select DistinguishedName
You should be able to do this quite easily. Add a reference to System.DirectoryServices.AccountManagement and then use this code:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, 'YourDomain'))
{
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity (ctx, "name");
if (computer != null)
{
// do whatever you need to do with your computer principal
string distinguishedName = computer.DistinguishedName;
}
}
Update: if you don't know your domain ........ - you can also use:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
in which case the principal context is created for the current domain you're located in.
You can use C# in the following manner
Connect to the Domain controller and get the DomainContext
Use that to look up the computer objects based on a name.
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Environment.UserDomainName)
{
using (PrincipalSearcher srch = new PrincipalSearcher(new ComputerPrincipal(ctx) { Name = "ServerName"}))
{
return srch.FindAll().Cast<ComputerPrincipal>().ToList().Select(x => x.DistinguishedName);
}
}
Above returns a list of DistinguishedNames that matches the Server Name.
All the other answers suggest using the System.DirectoryServices.AccountManagement namespace. While that will work, it is really just a wrapper around the System.DirectoryServices namespace to make things a bit easier to use. It does make things easier (sometimes) but it does so at the cost of performance.
For example, in all the examples you've been given, your code will retrieve every attribute with a value from the computer object in AD, even though you only want one attribute.
If you use DirectorySearcher, you can make the search and retrieve only that one attribute that you want:
public string GetComputerDn(string computerName) {
var searcher = new DirectorySearcher {
Filter = $"(&(objectClass=computer)(sAMAccountName={computerName}$))",
PropertiesToLoad = { "distinguishedName" } //return only the distinguishedName attribute
};
var result = searcher.FindOne();
if (result == null) return null;
return (string) result.Properties["distinguishedName"][0];
}
Note that in AD, the sAMAccountName of computer objects is what you would normally refer to as the "computer name", followed by $, which is why the filter is what it is.
Please try this:
Add reference to Active Directory Services (%programfiles%\Reference Assemblies\Microsoft\Framework.NETFramework\\System.DirectoryServices.AccountManagement.dll)
public string GetComputerName(string computerName)
{
using (var context = new PrincipalContext(ContextType.Domain, "your domain name goes here"))
{
using (var group = GroupPrincipal.FindByIdentity(context, "Active Directory Group Name goes here"))
{
var computers = #group.GetMembers(true);
return computers.FirstOrDefault(c => c.Name == computerName).DistinguishedName;
}
}
return null; // or return "Not Found"
}
I'm trying to search using the System.DirectoryServices.AccountManagement library in c#. The goal is to find an AD user with the pager field containing a string.
For example, if I have .pager = "F1234b!#" I need to find a user who's pager field contains "1234".
I can't figure out how to search the contents of the pager field in s.ds.am to contain a string.
If you want to experiment with S.DS.AM, try using this method. Put a breakpoint at the return listPrincipal line and inspect each of the principal variables for what they contain.
private static List<Principal> GetPrincipalList (string strPropertyValue, string strDomainController)
{
List<Principal> listPrincipal = null;
Principal principal = null;
GroupPrincipal groupPrincipal = null;
UserPrincipal userPrincipal = null;
ComputerPrincipal computerPrincipal = null;
PrincipalSearchResult<Principal> listPrincipalSearchResult = null; // Groups
PrincipalContext principalContext = null;
ContextType contextType;
IdentityType identityType;
try
{
// Setup a UserPrincipal list.
listPrincipal = new List<Principal>();
// Set the contextType to Domain because we are going through the AD directory store.
contextType = ContextType.Domain;
// Setup a domain context.
principalContext = new PrincipalContext(contextType, strDomainController);
// Setup the IdentityType. This is required, otherwise you will get a MultipleMatchesException error that says "Multiple principals contain a matching Identity."
// This happens when you have two objects that AD thinks match whatever you're passing to UserPrincipal.FindByIdentity(principalContextDomain, strPropertyValue)
// Use IdentityType.Guid because GUID is unique and never changes for a given object.
identityType = IdentityType.Guid;
// Find user.
principal = Principal.FindByIdentity(principalContext, identityType, strPropertyValue);
groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
userPrincipal = UserPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
computerPrincipal = ComputerPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
// Return the listPrincipal list.
return listPrincipal;
}
finally
{
// Cleanup objects.
listPrincipal = null;
listPrincipalSearchResult = null;
principalContext = null;
groupPrincipal = null;
userPrincipal = null;
computerPrincipal = null;
}
}
Use the * character as a wildcard. For example, in the filter criteria for DirectoryEntry, you can use the following:
(&(objectCategory=person)(objectClass=user)(pager=*1234*))
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
https://msdn.microsoft.com/en-us/library/ms679102(v=vs.85).aspx
Also, be careful using S.DS.AM. I know it provides a lot of useful functionality, but it is much slower than S.DS.AD.
Active Directory: The Principal Class - S.DS.AM vs S.DS.AD
Update:
Keep in mind that Microsoft introduced the following classes in the S.DS.AM namespace wrapper:
UserPrincipal
ComputerPrincipal
GroupPrincipal
https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.principal.aspx
Study the inheritance hierarchy and inspect the properties/attributes that are available -
WYSWIG. If the attribute that you want to search for is not included then you will not be able to use S.DS.AM - you will need to use S.DS.AD.
The * wildcard still stands when searching for properties that are available within the Principal class that contain specific character strings.
A useful post when searching on available properties in S.DS.AM: search by samaccountname with wildcards
Update 2:
I ran across the following post on SO that might be useful for what you are trying to do: How to get Active Directory Attributes not represented by the UserPrincipal class
I am trying to get users who are members of a particular group in AD.
I am trying with the following filters but i am unsuccessful.
1)
DirectoryEntry entry1 = new DirectoryEntry("LDAP://DC=xxinfo,DC=com");
string query = "(&(objectCategory=person)(objectClass=user)(memberOf=CN=Guests))";
2)
objSearchADAM.Filter = "(&(ObjectClass=user)(memberOf=CN=Network Configuration perators,CN=Builtin,DC=xxxx,DC=xxinfo,DC=com))";
also, is there any way to get users using this approach?
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "group_name");
this searches the current domain, i want to search the whole forest.is there any way to initialize ctx for whole forest?
any help would be appreciated.
thank you.
Got the answer!!
used below code
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "subdomain.domain.comany.com:3268", "DC=comapny,DC=com"))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "Group_Name");
port 3268 is used to search in global catalog.
I'm not a programmer by nature so I apologize in advance. I've searched far and wide and have found bits and pieces of 10 different ways to do one thing. What I'm trying to do seems very simple but I'm missing it...I need to search Active Directory using a first name and last name and display all users who match in a listbox. Can someone point me in the right direction, or if someone has already asked the same question link me to it? Thanks in advance!
Try something like this:-
DirectorySearcher d = new DirectorySearcher(somevalue);
d.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenname={0})(sn={1}))", firstname, lastname);
Also from How to search for users in Active Directory with C#
//Create a shortcut to the appropriate Windows domain
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain,
"myDomain");
//Create a "user object" in the context
using(UserPrincipal user = new UserPrincipal(domainContext))
{
//Specify the search parameters
user.Name = "he*";
//Create the searcher
//pass (our) user object
using(PrincipalSearcher pS = new PrincipalSearcher())
{
pS.QueryFilter = user;
//Perform the search
using(PrincipalSearchResult<Principal> results = pS.FindAll())
{
//If necessary, request more details
Principal pc = results.ToList()[0];
DirectoryEntry de = (DirectoryEntry)pc.GetUnderlyingObject();
}
}
}
//Output first result of the test
MessageBox.Show(de.Properties["mail"].Value.ToString());
Of course shortly after posting I found my answer :)
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "servername","username","password");
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = "fname";
qbeUser.Surname = "lname";
// qbeUser.DisplayName= "fname lname";
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach (var found in srch.FindAll())
{
lstUser.Items.Add(found.ToString());
}
here is the link: Search Users in Active Directory based on First Name, Last Name and Display Name
When I try to update the Name field (corresponds to the CN) on UserPrincipal (Principal, really), I get an error "The server is unwilling to process the request" on the call to UserPrincipal.Save().
I've checked to make sure there isn't another object in the same OU with the same Name (CN).
The PrincipalContext I'm operating at is the domain root (not exactly at the OU level where the user account exists).
What reason might there be for this error? Is it something that might be security policy related (even though I'm able to update all the other fields)?
using (var context = new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["domain"], ConfigurationManager.AppSettings["rootDN"], ContextOptions.Negotiate, ConfigurationManager.AppSettings["username"], ConfigurationManager.AppSettings["password"])) {
var user = UserPrincipal.FindByIdentity(context, IdentityType.Sid, "..."); // SID abbreviated
user.Name = "Name, Test";
user.Save();
}
The user I am using to create the PrincipalContext has the security rights to modify AD objects. If I update any other of the other fields (e.g. Surname, GivenName), everything works fine.
EDIT:
I've been able to accomplish what I need to do (using ADSI), but I have to run the following code under impersonation. The impersonation code is ugly, and the code below breaks away from the other way I'm updating AD data (using DirectoryServices.AccountManagement), so I'd like to get a better solution.
using (var companyOU = new DirectoryEntry("LDAP://" + company.UserAccountOU)) {
companyOU.Invoke("MoveHere", "LDAP://" + user.DistinguishedName, "cn=Name\, Test");
}
This is a cleaner way
using (var context = new PrincipalContext(ContextType.Domain))
{
var group = GroupPrincipal.FindByIdentity(context, groupName);
group.SamAccountName = newGroupName;
group.DisplayName = newGroupName;
group.Save();
var dirEntry = (DirectoryEntry)group.GetUnderlyingObject();
dirEntry.Rename("CN=" + newGroupName);
dirEntry.CommitChanges();
}
The only way I've found to do this is in the EDIT section in my question. Basically, you cannot use the UserPrincipal class. There is something special about the CN attribute, and you need to drop down a level and use DirectoryEntry, an LDAP string, and invoke the "MoveHere" ADSI command to rename the user account.