I'm having issues creating computers via LDAP in C#:
The following is my code:
C#
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix, GlobalVar.adUser, GlobalVar.adUserPassword);
DirectoryEntry newComputer = dirEntry.Children.Add("CN=" + computerName, "computer");
newComputer.Properties["samaccountname"].Value = computerName;
newComputer.Properties["dnshostname"].Value = computerName + ".[privacy].[domain].[here]";
newComputer.Properties["description"].Value = GlobalVar.adUser;
newComputer.Properties["location"].Value = "IT";
This works flawlessly with one exception: the computer is created in the correct folder. However the primary group is "Domain Users" instead of "Domain computers" when I create a computer directly in AD, the computer is automatically assigned the primary group "Domain Computers"
The result is that I cannot add the computer to the domain without editing it manually in ad.
Any solutions?
Best Regards,
Julian
I would use System.DirectoryServices.AccountManagement to do this...
LDAPusername = username with permissions to edit LDAP.
Pass the computername once it's created, then pass the group.
Sorry if this isn't perfect, this is my vb.net code I converted.
//The following code changes the principal group of an existing computer
PrincipalContext pc1 = new PrincipalContext(
ContextType.Domain,
"subdomain.domain.com",
LDAPusername,
LDAPpassword
);
dynamic cp = ComputerPrincipal.FindByIdentity(pc1, "computername");
dynamic computer = (DirectoryEntry)cp.GetUnderlyingObject();
// distinguishedname = "CN=Domain Users,CN=Users,DC=domain,DC=com"
string #group = "groupdistinguishedname";
DirectoryEntry groupdirentry = new DirectoryEntry(
"LDAP://" + #group,
LDAPusername,
LDAPpassword
);
groupdirentry.Invoke("Add", new object[] { computer.Path });
groupdirentry.CommitChanges();
groupdirentry.Invoke(
"GetInfoEx",
new object[] {
new object[] { "primaryGroupToken" },
0
}
);
object primaryGroupToken = groupdirentry.Invoke(
"Get",
new object[] { "primaryGroupToken" }
);
computer.Invoke(
"Put",
new object[] {"primaryGroupID",primaryGroupToken}
);
computer.CommitChanges();
You need to set the primaryGroupId to 515 I believe (Domain Computers)
newComputer.Properties["primaryGroupId"].Value = 515
I was having the same issue and was brought here initially after searching the web. Found the solution here.
To get a "valid" computer object, you have to set the attribute userAccountControl to 0x1020 = (PASSWD_NOTREQD | WORKSTATION_TRUST_ACCOUNT) and it's also recommended to set the sAMAccountName to the computername (in uppercase) followed by a '$' (same as if you create the object from the Management Console).
newComputer.Properties["userAccountControl"].Value = 0x1020;
This resolved the issue for me.
Related
I have a VMWare machine with Windows Server 2012 and Active Directory installed. The domain name is "cpx.local" and I have created a new user "testad".
I have a C# Winform application so I can test the connection to the LDAP server and then get all the users or groups in the Active Directory.
This is the code that works fine:
string server = "192.168.238.129";
string port = "389";
System.DirectoryServices.Protocols.LdapConnection ldapConnection =
new System.DirectoryServices.Protocols.LdapConnection(new LdapDirectoryIdentifier(server + ":" + port));
TimeSpan mytimeout = new TimeSpan(0, 0, 0, 1);
try
{
ldapConnection.AuthType = AuthType.Anonymous;
ldapConnection.AutoBind = false;
ldapConnection.Timeout = mytimeout;
ldapConnection.Bind();
Console.WriteLine(("Successfully authenticated to ldap server "));
ldapConnection.Dispose();
}
catch (LdapException ex)
{
Console.WriteLine(("Error with ldap server "));
Console.WriteLine((ex.GetType().ToString() + (":" + ex.Message)));
}
The problem is that if I want to authenticate with the new user "testad" it doesn't work.
I change the AuthType to be Basic and set the credentials.
ldapConnection.AuthType = AuthType.Basic;
ldapConnection.Credential = new NetworkCredential(#"cpx\testad", "test#D12345", "cpx.local");
ldapConnection.AutoBind = false;
ldapConnection.Timeout = mytimeout;
ldapConnection.Bind();
I get the following error:
I have tried to Login the Windows Server 2012 with this user and I can login perfect.
The interesting thing is that the following code is working fine:
var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", "192.168.238.129:389", "DC=cpx,DC=local"), "testad", "test#D12345");
var searcher = new DirectorySearcher(dirEntry)
{
Filter = "(&(&(objectClass=user)(objectClass=person)))"
};
var resultCollection = searcher.FindAll();
Am I doing something wrong with the NetworkCredentials?
maybe doubleccheck credentials.in NetworkCredential support username without 'cpx/' in front. as domain is provided
ldapConnection.Credential = new NetworkCredential(#"testad", "test#D12345", "cpx.local");
If you set the AuthType to Negotiate, does it work ?
AuthType details here
change:
ldapConnection.AuthType = AuthType.Basic;
to:
ldapConnection.AuthType = AuthType.Negotiate;
Regarding the domain name - cpx vs cpx.local - you can take a look at this article about some recommended practices
http://www.mdmarra.com/2012/11/why-you-shouldnt-use-local-in-your.html
The correct way to name an Active Directory domain is to create a subdomain that is the delegation of a parent domain that you have registered and have control over. As an example, if I ever started a consulting business and used the Internet-facing website mdmarra.com as my company's site, I should name my Active Directory domain ad.mdmarra.com or internal.mdmarra.com, or something similar. You want to avoid making up a TLD like .local and you also want to avoid the headache of using mdmarra.com for the Internet-facing zone and the internal zone.
Change: ldapConnection.AutoBind= false;
to: ldapConnection.AuthType = true;
I want to connect to our local Active Directory with C#.
I've found this good documentation.
But I really don't get how to connect via LDAP.
Can somebody of you explain how to use the asked parameters?
Sample Code:
static DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
DirectoryEntry ldapConnection = new DirectoryEntry("rizzo.leeds-art.ac.uk");
ldapConnection.Path = "LDAP://OU=staffusers,DC=leeds-art,DC=ac,DC=uk";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
I just have the Hostname and the IP Address of our Active Directory Server. What does DC=xxx,DC=xx and so on mean?
DC is your domain. If you want to connect to the domain example.com than your dc's are: DC=example,DC=com
You actually don't need any hostname or ip address of your domain controller (There could be plenty of them).
Just imagine that you're connecting to the domain itself. So for connecting to the domain example.com you can simply write
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
And you're done.
You can also specify a user and a password used to connect:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com", "username", "password");
Also be sure to always write LDAP in upper case. I had some trouble and strange exceptions until I read somewhere that I should try to write it in upper case and that solved my problems.
The directoryEntry.Path Property allows you to dive deeper into your domain. So if you want to search a user in a specific OU (Organizational Unit) you can set it there.
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
directoryEntry.Path = "LDAP://OU=Specific Users,OU=All Users,OU=Users,DC=example,DC=com";
This would match the following AD hierarchy:
com
example
Users
All Users
Specific Users
Simply write the hierarchy from deepest to highest.
Now you can do plenty of things
For example search a user by account name and get the user's surname:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
DirectorySearcher searcher = new DirectorySearcher(directoryEntry) {
PageSize = int.MaxValue,
Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=AnAccountName))"
};
searcher.PropertiesToLoad.Add("sn");
var result = searcher.FindOne();
if (result == null) {
return; // Or whatever you need to do in this case
}
string surname;
if (result.Properties.Contains("sn")) {
surname = result.Properties["sn"][0].ToString();
}
ldapConnection is the server adres: ldap.example.com
Ldap.Connection.Path is the path inside the ADS that you like to use insert in LDAP format.
OU=Your_OU,OU=other_ou,dc=example,dc=com
You start at the deepest OU working back to the root of the AD, then add dc=X for every domain section until you have everything including the top level domain
Now i miss a parameter to authenticate, this works the same as the path for the username
CN=username,OU=users,DC=example,DC=com
Introduction to LDAP
If your email address is 'myname#mydomain.com', try changing the createDirectoryEntry() as below.
XYZ is an optional parameter if it exists in mydomain directory
static DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
DirectoryEntry ldapConnection = new DirectoryEntry("myname.mydomain.com");
ldapConnection.Path = "LDAP://OU=Users, OU=XYZ,DC=mydomain,DC=com";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
This will basically check for com -> mydomain -> XYZ -> Users -> abcd
The main function looks as below:
try
{
username = "Firstname LastName"
DirectoryEntry myLdapConnection = createDirectoryEntry();
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(cn=" + username + ")";
....
So, I'm kind of stuck here...
I'm writing a program that should be able to list all users in the local administrator group on a MS Windows Server 2008 R2.
The problem here is that I'm only allowed to use .NET 2.0 - so I'm not able to use the GroupPrincipal Class... Which would have made this a really easy task.
Any pointers would be appriciated!
Cheers!
Jeez!
Don't know what I was thinking really - it's so simple!
All creds to Masoud Tabatabaei - found the following codesnippet on:
http://csharptuning.blogspot.se/2007/09/how-to-get-list-of-windows-user-in-c.html
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName);
DirectoryEntry admGroup = localMachine.Children.Find("administrators","group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
lstUsers.Items.Add(member.Name);
}
Did you try WMI ?
for example
ManagementObjectSearcher search = new ManagementObjectSearcher(#"SELECT * FROM Win32_UserAccount where LocalAccount = true");
ManagementObjectCollection userList = search.Get();
foreach (ManagementObject user in userList)
{
Console.WriteLine("User name: {0}, Full Name: {1}",
user["Name"].ToString(), user["FullName"].ToString());
}
Will give you a list of users in local SAM. You can add other attributes to the query and refine your list.
Do not forget to add a reference to System.Management.dll
If your are still looking for an answer, here:
If you'd like to get the administrator group, you can use this code:
public static DirectoryEntry GetLocalAdminstratorGroup()
{
using (var WindowsActiveDirectory = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer"))
{
return WindowsActiveDirectory.Children.Find(GetLocalizedAdministratorGroupName(), "group");
}
}
//Localized == Language Independent
public static string GetLocalizedAdministratorGroupName()
{
//For English Windows version, this equals "BUILTIN\Administrators".
var adminGroupName = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)).Value;
//Remove the "BUILTIN\" part, get the local name of the group
return adminGroupName.Split('\\')[1];
}
If you'd also like to enumerate it (like you need a username), you can do this, using the methods before:
object members = AdminGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
Console.WriteLine(member.Name);
}
I posted a question re LDAP account management, but after exploring this, it's not what i'm after. I've managed to find two ways of creating users on a machine, and i find one is much neater than the other, however, i am uncertain how to convert the first option over to the second option entirely.
This was my first solution:
Process MyProc = new Process();
MyProc.StartInfo.WorkingDirectory = System.Environment.SystemDirectory;
MyProc.StartInfo.FileName = "net.exe";
MyProc.StartInfo.UseShellExecute = false;
MyProc.StartInfo.RedirectStandardError = true;
MyProc.StartInfo.RedirectStandardInput = true;
MyProc.StartInfo.RedirectStandardOutput = true;
MyProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
MyProc.StartInfo.Arguments = string.Format(#" user {0} {1} /ADD /ACTIVE:YES /EXPIRES:NEVER /FULLNAME:{0}"" /PASSWORDCHG:NO /PASSWORDREQ:YES", username, password);
MyProc.Start();
MyProc.WaitForExit();
int exit = MyProc.ExitCode;
MyProc.Close();
return exit == 0;
And this was my second (preffered) solution:
DirectoryEntry root = GetDELocalRoot();
DirectoryEntry user = root.Children.Add(username, "user");
//TODO: Always Active
//TODO: Never Expires
//TODO: No Password Change
//TODO: Password Required
user.Properties["description"].Value = "Account for running the MicaService and handling updates.";
user.Invoke("SetPassword", new object[] { password });
user.CommitChanges();
user.Close();
I would like to map all the settings in my TODO: from the first solution into my second neater solution.
I have tried the following line as well:
user.Properties["userAccountControl"].Value = ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT | ADS_USER_FLAG.ADS_UF_PASSWD_CANT_CHANGE | ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD;
But this does not work as the property does not exist in cache.
NOTE: the GetDELocalRoot() = return new DirectoryEntry("WinNT://" + Environment.MachineName);
Thanks for any input!
Regards
Tris
Check out my friend Richard Mueller's web site which has lots of useful information and reference material on what those two providers - WinNT for local machine accounts vs. LDAP for network accounts - have to offer.
There's also a Excel sheeet with all attributes that the WinNT provider exposes - it's a lot less than what the LDAP provider has, so I'm not sure if you'll be able to set all the properties you're looking for.
Marc
The User.Identity.Name property returns the domain login id.
Which class/property exposes the actual user name?
For user "John Doe" who logs into the web application supplying my_domain\jdoe
**User.Identity.Name -**
Returns : *my_domain\jdoe*
**System.Environment.UserName**
Returns: *jdoe*
Which class/property returns? ... "John Doe"
If you are thinking Active Directory, you'll need to find the UserPrincipal that corresponds to the given samAccountName and get the DisplayName property from it. Note that it may not be set.
string fullName = null;
using (PrincipalContext context = new PrincipalContext( ContextType.Domain ))
{
using (UserPrincipal user
= UserPrincipal.FindByIdentity( context,
User.Identity.Name ))
{
if (user != null)
{
fullName = user.DisplayName;
}
}
}
Sounds like instead of the login name, you are after the display name of an Active Directory user account. What you might want to do is to do an AD search (DirectorySearcher) and get the display name from the search result property.
I'm assuming that you are in an AD environment, since you tagged the question adsi.
Note: If you are working with .NET 3.5, you might want to look at tvanfosson's post.
The IIdentity interface is that which provides the Name property on User.Identity. The IIdentity interface can be implemented on any number of classes which know how to lookup users from a data-store (SQL Server, Active Directory, etc).
There is no property of the IIdentity interface which provides "John Doe". If that information is located in your data-store then you'll need to use the tools specific to that data-store to access it.
That said, its entirely possible that the object which is returned by User.Identity has a property which contains "John Doe" that you might be able to access through some other interface besides IIdentity (our custom IIdentity implementation does this, for example).
using System.DirectoryServices;
public static string GetFullName(string strLogin)
{
string str = "";
string strDomain;
string strName;
// Parse the string to check if domain name is present.
int idx = strLogin.IndexOf('\\');
if (idx == -1)
{
idx = strLogin.IndexOf('#');
}
if (idx != -1)
{
strDomain = strLogin.Substring(0, idx);
strName = strLogin.Substring(idx + 1);
}
else
{
strDomain = Environment.MachineName;
strName = strLogin;
}
DirectoryEntry obDirEntry = null;
try
{
obDirEntry = new DirectoryEntry("WinNT://" + strDomain + "/" + strName);
System.DirectoryServices.PropertyCollection coll = obDirEntry.Properties;
object obVal = coll["FullName"].Value;
str = obVal.ToString();
}
catch (Exception ex)
{
str = ex.Message;
}
return str;
}
and the you can just call
var strJonDoeName = GetFullName(User.Identity.Name)
code mock it from here
Maybe I have made a mistake somewhere, but WinNT://... hasn't worked for domain accounts for me. Additionally, if the user isn't in the same domain as the machine, than PrincipalContext may not find the wanted element (as it searches in the current context, if used as above).
The following should translate the "friendly domain name" as provided by User.Identity.Name to an ldap compliant domain. Using the distinguishName of the domain delivers the needed ldap path information (which has solved my cross domain problem):
(VB.NET, sorry)
Imports System.DirectoryServices
Imports System.DirectoryServices.AccountManagement
Imports System.DirectoryServices.ActiveDirectory
...
Dim strUserName As String
Dim objDirContext As DirectoryContext
Dim objContext As PrincipalContext
Dim objPrincipal As Principal
Dim strLDAPDomainPath As String
...
// User.Identity.Name delivers domain\account or account#domain
// Split User.Identity.Name in domain and account as specified above
strDomain = "my_domain"
strAccount = "jdoe"
// Get LDAP domain relative path (distinguishName) from short domain name (e.g. my_domain -> us.my_domain.com -> DC=us,DC=my_domain,DC=com)
Try
objDirContext = New DirectoryContext(DirectoryContextType.Domain, strDomain)
strLDAPDomainPath = Domain.GetDomain(objDirContext).GetDirectoryEntry.Properties("distinguishedName").Value.ToString
Catch objException As DirectoryServicesCOMException
Throw New Exception("Couldn't get LDAP domain: " & objException.Message)
End Try
// Find user in LDAP
// Nothing results in using current domain controller
Try
objContext = New PrincipalContext(ContextType.Domain, Nothing, strLDAPDomainPath)
objPrincipal = Principal.FindByIdentity(objContext, IdentityType.Name, strAccount)
If Not IsNothing(objPrincipal) Then
strUserName = objPrincipal.DisplayName
End If
Catch objException As Exception
Throw New Exception("Couldn't get user display name: " & objException.Message)
End Try
// strUserName should now contain the wanted full name