In my MVC-5 application I am adding/editing users in active directory through my application. Whenever I make changes in AD, each time I need to pass ldapUserName(Admin) and ldapPassword(pass@123) to connect to Active directory. Then only I can perform operation in AD. In place of passing credentials each time I would like to use Service account (domain\service_account) under which my application in running in order to connect to AD. How to achieve that ??
-
Thanks in advance ! – Kumar Oct 17 '16 at 12:45
-
how do you connect to AD – lordkain Oct 17 '16 at 12:55
-
Have you tried setting the app pool account as the AD Admin account? You may need to grant add'l folder perms for this account to access the site folder(s). – Stinky Towel Oct 17 '16 at 13:01
-
try { using (var directoryEntry = new DirectoryEntry(ldabPath, ldapUserName, ldapPassword)) { using (var newUser = directoryEntry.Children.Add("CN=" + userViewModel.UserName, "User")) { newUser.CommitChanges(); newUser.Invoke("setpassword", userViewModel.Password); newUser.Properties["userAccountControl"].Value = 0x0200; newUser.CommitChanges(); } } – Kumar Oct 17 '16 at 13:06
-
above code is being used to add a user in AD. Instead of passing credentials I want to use service account. I want a seamless access to AD using service account. – Kumar Oct 17 '16 at 13:07
-
Check this out as well: http://stackoverflow.com/a/37565248/2779990 – Stinky Towel Oct 17 '16 at 13:09
-
I checked it, but could not find my answer. Without using credential, how you can connect to AD. – Kumar Oct 18 '16 at 05:55
2 Answers
You can choose one of 2 options.
Option 1. You run your application on a computer which is joined to a domain, where you add\remove users from. In this case the simplest solution is to run your application (application pool) under the domain account with sufficient permissions (in simple way can be an account that belongs to Domain Admins group. Will be also an admin on the host, where application runs)
Option 2. You run your application on a standalone computer, that is not joined to a domain. In this case you can choose one of the following:
A. Impersonate your thread to act as the domain account when performing all network connections. You need to use LogonUser function with LOGON32_LOGON_NEW_CREDENTIALS flag. The drawback of this method is that all network connections (e. g. you connect to a network share) will be made under domain account. For more details on implementation, see this post.
B. Create a connection manager which will create DirectoryEntry for you with required credentials. See the code below:
public interface IDirectoryEntryManager
{
DirectoryEntry GetDirectoryEntry(string domain, string baseDn);
}
public interface ICredentialProvider
{
Credential GetCredential(string domain);
}
public class Credential
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class DirectoryEntryManager : IDirectoryEntryManager, IDisposable
{
private class DomainConnectionInfo
{
internal DomainConnectionInfo(string server, Credential credential)
{
Server = server;
Credential = credential;
}
internal string Server { get; private set; }
internal Credential Credential { get; private set; }
}
private bool disposed;
ICredentialProvider _credentialProvider;
Dictionary<string, DomainConnectionInfo> connectionsInfo = new Dictionary<string, DomainConnectionInfo>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, DirectoryEntry> connections = new Dictionary<string, DirectoryEntry>(StringComparer.OrdinalIgnoreCase);
public DirectoryEntryManager(ICredentialProvider credentialProvider)
{
_credentialProvider = credentialProvider;
}
public DirectoryEntry GetDirectoryEntry(string domain, string baseDn)
{
if (disposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
return GetOrCreateConnection(domain, baseDn);
}
public void Dispose()
{
if (!disposed)
{
foreach (var connection in connections)
{
connection.Value.Dispose();
}
connections.Clear();
disposed = true;
}
}
private DirectoryEntry GetOrCreateConnection(string domain, string baseDn)
{
DomainConnectionInfo info;
if (!connectionsInfo.TryGetValue(domain, out info))
{
var credential = _credentialProvider.GetCredential(domain);
var dc = DomainController.FindOne(new DirectoryContext(DirectoryContextType.Domain, credential.UserName, credential.Password));
info = new DomainConnectionInfo(dc.Name, credential);
// maintaining a connection to rootDse object to make all LDAP queries use this single connection under the hood. Increasing performance
var entry = new DirectoryEntry(string.Format("LDAP://{0}/RootDSE", dc.Name));
entry.RefreshCache();
connections.Add(domain, entry);
connectionsInfo.Add(domain, info);
}
return new DirectoryEntry(string.Format("LDAP://{0}/{1}", info.Server, baseDn), info.Credential.UserName, info.Credential.Password);
}
}
Didn't test the code. Use server bind instead of serverless bind (e. g. LDAP://domain.com) is better in case if you create a user in one part of program and try to access it in another part. Using serverless bind you can connect to different DCs, so the user you are trying to access may be not replicated to 2nd DC.
Be aware that the domain controller may become unavailable, so you need to implement logic, to search for another DC and refresh your connections cache if required.
You may store credential in a file\LSA possibly encrypted and make DirectoryEntryManager class singleton.
Simply creating the DirectoryEntry
by new DirectoryEntry(ldabPath, null, null)
will use credential of current user (service account in case of Windows service). The key is passing null to both username and password.
Is that what you want?
Just make sure the service account has enough permission to set password for all users involved.

- 1,707
- 1
- 14
- 23