How do I set the ManagedBy property on a GroupPrincipal - c#

I'm creating and updating Groups in Active Directory using the GroupPrincipal class in System.DirectoryServices.AccountManagement. When creating and updating, I also need to be able to set the ManagedBy property that you are able to set in the Managed By tab in the groups properties in the AD management console.
Can it be done programatically?

You cannot do this directly, unfortunately - but you can get access to the underlying DirectoryEntry and do it there:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
UserPrincipal toBeModified = UserPrincipal.FindByIdentity(".....");
UserPrincipal manager = UserPrincipal.FindByIdentity(ctx, "......");
DirectoryEntry de = toBeModified.GetUnderlyingObject() as DirectoryEntry;
if (de != null)
{
de.Properties["managedBy"].Value = manager.DistinguishedName;
toBeModified.Save();
}

You could extend the GroupPrincipal class and provide a ManagedBy property using the ExtensionSet method.

Take a look at this page. This is one of the best tutorials on AD in c#.
Some code that should work(untested) :
string connectionPrefix = "LDAP://" + ouPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newGroup = dirEntry.Children.Add
("CN=" + groupName, "group");
group.Properties["sAmAccountName"].Value = groupName;
newGroup.Properties["managedBy"].Value = managerDistinguishedName;
newGroup.CommitChanges();
dirEntry.Close();
newGroup.Close();

Related

Set Active Directory user properties using C#

I am trying to set manager, department and title property of a user in active directory using C#.
Below is my code.
using (var context = new PrincipalContext(ContextType.Domain, "DomainName", "UserName", "Password"))
{
using (var userPrincipal = new UserPrincipal(context))
{
userPrincipal.SamAccountName = "UserSamACcountName";
using (PrincipalSearcher search = new PrincipalSearcher(userPrincipal))
{
UserPrincipal result = (UserPrincipal)search.FindOne();
DirectoryEntry directoryEntry = result.GetUnderlyingObject() as DirectoryEntry;
directoryEntry.Properties["manager"].Value = "<Manager Name>";
directoryEntry.Properties["title"].Value = "<Designation>";
directoryEntry.Properties["department"].Value = "<Department>";
directoryEntry.CommitChanges();
}
}
}
But I am getting below error when committing the changes.
A constraint violation occurred.
After debugging I found out that these properties (manager,title,department) are not available in DirectoryEntry properties collection. I could set "mailNickName" property without any error.
Does anyone has any solution?
Your code is technically correct, but you are sending an illigal value for the managerproperty.
The manager property is a distinguished name, not just the name of the person
directoryEntry.Properties["manager"].Value = "John Doe";
Will throw
A constraint violation occurred.
Change your code to something like:
directoryEntry.Properties["manager"].Value = "uid=john.doe,ou=People,dc=example,dc=com";

Get domain name from PrincipalContext

Assume that we have this context
private static readonly PrincipalContext Context =
new PrincipalContext(ContextType.Domain, "255.255.255.252",
"OU=TestOrgUnit,DC=as,DC=asf",
"blabla", "12345");
I'm searching for users in this domain. I get their's names as SomeNickName, but they should be DomainName\SomeNickName.
Is it possible to get a DomainName from PrincipalContext object? I found a solution for DirectoryEntry, but cannot convert PrincipalContext into it.
This code
DirectoryEntry deBase = new DirectoryEntry("255.255.255.252", "AdminLogin", "PWD");
and this code
DirectoryEntry deBase = new DirectoryEntry("255.255.255.252://OU=TestOrgUnit,DC=as,DC=asf", "AdminLogin", "PWD");
throws an exception and doesn't work.
So technically you have the domain info in the DN you've specified for the connecting OU (DC=as,DC=asf). The first DC is the pre-Win2K name which seems to be what you're looking for.
As far as the PrincipalContext itself containing the domain info it seems that it doesn't.
If you want to use the DE to get more properties or to do your user search, you need to create it like this:
var deBase = new DirectoryEntry("LDAP://255.255.255.252/OU=TestOrgUnit,DC=as,DC=asf", "AdminLogin", "PWD")

Search Users in Specific OU Active Directory

I have different OU in my Active Directory for different users, I want to get all users of a specific OU using C#.
Currently I have this filter, but it returns all users from all OU
(&(objectClass=User)(objectCategory=Person))
Kindly help me in finding users of specific user using ldap
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// LDAP string to define your OU
string ou = "OU=Sales,DC=YourCompany,DC=com";
// set up a "PrincipalContext" for that OU
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "Yourcompany.com", ou))
{
// define the "query-by-example" user (or group, or computer) for your search
UserPrincipal qbeUser = new UserPrincipal(ctx);
// set whatever attributes you want to limit your search for, e.g. Name, etc.
qbeUser.Surname = "Smith";
// define a searcher for that context and that query-by-example
using (PrincipalSearcher searcher = new PrincipalSearcher(qbeUser))
{
foreach (Principal p in searcher.FindAll())
{
// Convert the "generic" Principal to a UserPrincipal
UserPrincipal user = p as UserPrincipal;
if (user != null)
{
// do something with your found user....
}
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.
One option is to just set the organization unit (OU) when you create your DirectoryEntry object:
using (var entry = new DirectoryEntry($"LDAP://OU={unit},OU=Accounts,DC={domain},DC=local"))
{
// Setup your search within the directory
var search = new DirectorySearcher(entry)
{
Filter = "(&(objectCategory=person)(objectClass=user)(memberOf=*))"
};
// Set the properties to be returned
search.PropertiesToLoad.Add("SamAccountName");
// Get the results
var results = search.FindAll();
// TODO Process the results as needed...
}

Get just the Enabled Accounts from Active Directory

I am using System.DirectoryServices.AccountManagement.dll to deal with Active Directory
to get all the users in the "Domain Users" group.
This is returning all the users in the domain but I need to get just the enabled ones.
Here is some sample code:
List<string> users = new List<string>();
PrincipalContext pcContext = GetPrincipalContext();
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcContext,
IdentityType.Name,
"Domain Users");
foreach (Principal user in grp.GetMembers(true).OfType<UserPrincipal>())
{
if (user.Enabled != false)
{
users.Add(user.Name);
}
}
Other groups work fine, but when the group is "Domain Users", the value of the Enabled property is false for all users. This makes it impossible to distinguish between enabled and disabled users without doing a further query for each user.
UserPrinciple objects have a bool Enabled property for this.
http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal_properties.aspx
// Add this to GetUserDetails
objUserDetails.EmployeeId = UserPrinical.EmployeeId;
// Then condition the add to only add enabled
if (objUserDetails.Enabled) {
objUserDetails.Add(GetUserDetails(p.Name));
}
A method around this problem could be to first search for Enabled Users using the PrincipalSearcher class and then use the Principal's method of IsMemberOf()
List<string> users = List<string>();
PrincipalContext pcContext = GetPrincipalContext();
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcContext, IdentityType.Name, "Domain Users");
UserPrincipal searchFilter = new UserPrincipal(pcContext){ Enabled = true }
PrincipalSearcher searcher = new PrincipalSearcher(searchFilter);
PrincipalSearchResult<Principal> results = searcher.FindAll();
foreach (Principal user in results)
if (user.IsMemberOf(grp))
users.Add(user.SamAccountName);
There's a remark on the MSDN page of the Enabled property saying :
If the principal has not been persisted in the store, this property returns null. After the principal is persisted, the default enabled setting depends on the store. The AD DS and AD LDS stores disable new principals when they are persisted, whereas SAM enables new principals when they are persisted. The application can only set this property to a value after it has been persisted in the store.
Perhaps it's related if the default is false ?
Also, there's a post on the MSDN forum about UserPrincipal.Enabled returns False for accounts that are in fact enabled? and that really sound similar to your issue. According to the post there's perhaps a solution here :
I think I misunderstood. Disregard what I posted before. I think I
know what's happening. The GetMembers method apparently isn't loading
the UserPrincipal data. I don't know if there is a better solution,
but the following works (at least on my AD):
foreach (UserPrincipal user in group.GetMembers(false))
{
UserPrincipal tempUser = UserPrincipal.FindByIdentity(context, user.SamAccountName);
// use tempUser.Enabled
// other code here
}

How do I get the first name and last name of the logged in Windows user?

How I can get my first name last name with c# in my system (logging in windows with Active Directory username and pass)?
Is it possible to do that without going to the AD?
If you're using .Net 3.0 or higher, there's a lovely library that makes this practically write itself. System.DirectoryServices.AccountManagement has a UserPrincipal object that gets exactly what you are looking for and you don't have to mess with LDAP or drop to system calls to do it. Here's all it'd take:
Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal;
// or, if you're in Asp.Net with windows authentication you can use:
// WindowsPrincipal principal = (WindowsPrincipal)User;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain))
{
UserPrincipal up = UserPrincipal.FindByIdentity(pc, principal.Identity.Name);
return up.DisplayName;
// or return up.GivenName + " " + up.Surname;
}
Note: you don't actually need the principal if you already have the username, but if you're running under the users context, it's just as easy to pull it from there.
There is an easier way to do this:
using System.DirectoryServices.AccountManagement;
UserPrincipal userPrincipal = UserPrincipal.Current;
String name = userPrincipal.DisplayName;
This solution didn't work for me but this function worked great:
public static string GetUserFullName(string domain, string userName)
{
DirectoryEntry userEntry = new DirectoryEntry("WinNT://" + domain + "/" + userName + ",User");
return (string)userEntry.Properties["fullname"].Value;
}
You should call it that way:
GetUserFullName(Environment.UserDomainName, Environment.UserName);
(Found it here).
The problem with the approved answer is that if you have a policy of Lastname, Firstname in place, then DisplayName gives Smith, John, not John Smith. There are two ways to get the correct form, the userPrincipal.Name property contains "John Smith (jsmith1)" so you could use this, and just string.Split on "(". Or use the following:
private string ConvertUserNameToDisplayName(string currentSentencedByUsername)
{
string name = "";
using (var context = new PrincipalContext(ContextType.Domain))
{
var usr = UserPrincipal.FindByIdentity(context, currentSentencedByUsername);
if (usr != null)
name = usr.GivenName+" "+usr.Surname;
}
if (name == "")
throw new Exception("The UserId is not present in Active Directory");
return name;
}
This would give the required form "John Smith" as required by the original poster.
The fastest way is to bind directly to their AD object using their SID, which you already have. For example:
//ASP.NET
var identity = (WindowsIdentity) HttpContext.Current.User.Identity;
//Desktop
var identity = WindowsIdentity.GetCurrent();
var user = new DirectoryEntry($"LDAP://<SID={identity.User.Value}>");
//Ask for only the attributes you want to read.
//If you omit this, it will end up getting every attribute with a value,
//which is unnecessary.
user.RefreshCache(new [] { "givenName", "sn" });
var firstName = user.Properties["givenName"].Value;
var lastName = user.Properties["sn"].Value;
Had the same issue. After some research and browsing the Microsoft Docs, I found the solution.
First install the System.DirectoryServices.AccountManagement package using Nuget Package Manager.
Then while calling the GetUserNameWithoutDomain method pass the following as the parameters:
GetUserNameWithoutDomain(UserPrincipal.Current.GivenName, UserPrincipal.Current.Surname);
This should definitely work!

Categories

Resources