How to create GMSA account via C# - c#

I have tried to look for the c# code example to see how the AD service account is created but not much luck. Anyone can provide an example code for creating AD service account please?
I have tried UserPrincipal with $ at the end of the name but not much luck. Errors with Access Denied (Cant create under root MyDomain or under a CN)
// Domain Context to use specific LDAP path.
domainContext = new PrincipalContext(ContextType.Domain, domainContext.ConnectedServer, "CN=Managed Service Accounts,dc=mydomain");
UserPrincipal userAccount = new UserPrincipal(domainContext)
{
DisplayName = userName,
SamAccountName = $"{userName}$",
Description = description
};
userAccount.Save();

Little late, but I think I can answer it. You need to use the NetAddServiceAccount function through logoncli.dll. I hadn't been able to get it to work in PowerShell, even with adding what I thought was an appropriate type shim, but I just came across a module that seems to work.
https://github.com/beatcracker/Powershell-Misc/blob/master/Use-ServiceAccount.ps1
The C# code for the type definition in that script should have everything you need to implement it for yourself.

Related

C# access Active Directory as another user

I'd like to ask for your help with following issue. I am working on ad-hoc application, will be used only by me and just once.
Part of it is to perform password reset for over 3000 users in AD and send them new credentials.
I can read from AD as normal user, but I have to use privileged account to modify it. How can I do that? I know, I can use PowerShell and have it done in a seconds, but I'd like to learn how to do it in C#.
My code to search for user is simple
public class ADSecurity
{
public static string getUserName(string sam)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam);
return user.Name;
}
}
How can I do the same, but as different user?
I've seen some guides, but none of them explained a-z ... just advice on how to impersonate, but nothing about how to use it. There was one article here about impersonation, but using LDAP protocol (DirectoryEntry). But as I understand, it is really slow.
Any advice appreciated. I need to run it 2 days from now, so in worst case scenario I use PowerShell to do it.
Thanks.
There are a few ways to do it:
Run your application under the needed credentials (Shift+right-click on the .exe file and use 'Run as a different user'). If you're just doing this once, this is the easiest.
Use the PrincipalContext constructor that accepts a username and password.
public class ADSecurity
{
public static string getUserName(string sam)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, username, password);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam);
return user.Name;
}
}
Use the DirectoryEntry constructor that accepts a username and password.
I'm not sure which example you're talking about that's "slow", but in my experience, using DirectoryEntry directly is almost always faster, as long as you use it correctly. The System.DirectoryServices.AccountManagement namespace (what you are using in your example) uses DirectoryEntry behind the scenes anyway.
Of course, options 2 & 3 require you know the password.

AccountManagement create user return 'object already exists' exception

I'm trying to create a new UserPrincipal in a specific OU inside ActiveDirectory, and it returns an Exception with the message 'Object already exists'. (obviously) The user don't exists in that OU, and I'm testing its existence.
What am I doing wrong?
Here's the code throwing the exception:
public UserPrincipal CreateUser(string username, string pass,
string givenName, string surname) {
PrincipalContext context = this.principalContext;
UserPrincipal user = new UserPrincipal(context);
user.SamAccountName = username;
user.UserPrincipalName = username;
user.GivenName = givenName;
user.Surname = surname;
user.SetPassword(pass);
user.Save();
return user;
}
Edit 1: After unit tests, I found that the code is ok. I use this method in a lib (where I run the tests), that is called by another application, which has an Windows authentication mode enabled. Maybe the app is sending that authentication to AD?
I experienced the same error but didn't help much from the accepted answer above as in my case the issue is not due to sAMAccountName but due to Name. The account attempted to create had unique sAMAccountName but the name already exists which resulted in this error.
The object already exists.
Looks like the error can happen against more than one Active Directory account properties
sAMAccountName
Name
Recommendation:
Like in most of the cases, it is better to check for existence before creating a new entry.
Useful links:
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.directoryentries.find?view=dotnet-plat-ext-3.1
I hope this helps someone.
Cheers,
sAMAccountName must be unique across the enterprise. You mention 'specific OU' when creating the user. Is it possible you have another user with the same username/sAMAccountName in a different OU?

Setting user password using C# does not work, any ideas?

I need to reset a user password. to do so, I use the following code:
DirectoryEntry de = ..
de.AuthenticationType = AuthenticationType.Secure
de.Password = txtPassword.text
de.CommitChanges()
When i run the code - nothing happens. The user password does not change, and no exception is thrown.
if i use the following method:
de.Invoke("SetPassword", .. );
when i run the code, I get a message that says: Please insert smartcard ...
I have admin privilages over the user account.
The user does not have UAV set for smart card.
Any ideas ?
The Password property of the DirectoryEntry class isn't what you think it is. You're not changing the user's password, you're changing the password you're using to access further information from the DirectoryEntry object.
From the MSDN documentation:
You can set the Username and Password properties to specify alternate
credentials with which to access the information in Active Directory
Domain Services. Any other DirectoryEntry objects retrieved from this
instance (for example, through Children) are automatically created
with the same alternate credentials.
With your second method, if you're being asked to insert a smartcard I doubt that has anything to do with the user you're modifying - it's more likely it's asking for your smartcard. If you're not set up to use smartcards either, then I'm really not sure why it's asking you for one at all.
Take a look at this related question and see if the answers there help.

Is there a way to enable referral chasing for UserPrincipal.FindByIdentity()?

I have a .NET 3.5 web application that uses the System.DirectoryServices.AccountManagement classes. When I search for some users I get a PrincipalOperationException: A referral was returned from the server. If I did this the old school way with my own LDAP code I could enable chasing of referrals. Do I need to rewrite my code?
My code looks like this:
using (var principalContext = new PrincipalContext(ContextType.Domain, null, adPath))
{
// Find the principal object for which you wish to enumerate group
// membership.
using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, identity))
{
if (userPrincipal != null)
{
Name = userPrincipal.DisplayName;
DistinguishedName = userPrincipal.DistinguishedName;
EmailAddress = userPrincipal.EmailAddress;
Sid = userPrincipal.Sid.Value;
}
}
}
My adPath can be one of 2 values. One of the values is a domain that was recently joined, and can be accessed using different tools. I believe this is a problem with how this .NET library makes the LDAP calls.
Here is a partial Answer, as it's too long for a comment.
According to this Microsoft documentation, as you even know, Referrals are a hint that the client can chase. But concerning RODC they add "For example, in the case of an LDAP application, if chase referrals is enabled on the LDAP connection between the client and the RODC, the application never knows that the client received a referral from the RODC. The client is automatically redirected to the writable domain controller that is specified in the referral. ".
So I look how to enable LDAP chasing on a connexion in Microsoft site and I found this which means ADSI use. I'am very interested in the answer.
Do you try to query the global catalog like this :
/* Retreiving a principal context
*/
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "YourGCServer:3268", "dc=dom,dc=fr", "User", "Password");
It's supposed to contains all the forest domain's datas.
I hope it helps.
Have you tried code of the form(put the domain in as the second argument):
var principalContext = new PrincipalContext(ContextType.Domain, "office.local", "OU=Users, DC=office, DC=local" ))
Also make sure that the adPath is from most specific to least specific.

Modifying Active Directory Properties

I am trying to modify certain properties for users in our active directory. Some properties I can change and some I can't. I am doing impersonation, but for some of the properties I still get the "General Access denied error" when I try to call CommitChanges().
For example this will work:
DirectoryEntry deUser = new DirectoryEntry(result.Path);
if (ImpersonateValidUser(adConnectionUsername, adConnectionDomain, adConnectionPassword))
{
deUser.Properties["ampPasswordQuestion"].Value = newPasswordQuestion;
deUser.Properties["ampPasswordAnswer"].Value = newPasswordAnswer;
deUser.CommitChanges();
deUser.Close();
UndoImpersonation();
}
This works presumably because we've manually added the properties into the AD schema with no access restrictions. (See ASP.NET 3.5 Security, Membership, and Role Management with C# and VB)
However if I try to modify something like the Comment property of a record as follows:
DirectoryEntry deUser = new DirectoryEntry(result.Path);
if (ImpersonateValidUser(adConnectionUsername, adConnectionDomain, adConnectionPassword))
{
deUser.Properties["comment"].Value = comment;
deUser.CommitChanges();
deUser.Close();
UndoImpersonation();
}
Then I will get "General access denied error".
Anyone have any ideas?
The Impersionation code is from Microsoft found at: http://support.microsoft.com/kb/306158
I believe I have found the problem. Apparently you need to create the DirectoryEntry that will be modified after Impersonation is done. This seems to make sense because I believe the rights of the modifying user become effective at the creation of the entry to be modified. Can anyone verify this? I have not seen it explained this way in any documentation.
I did some more testing and found out that the first entry does not even require Impersonation because the those attributes are not secured.
In the 2nd case the following code works:
if (ImpersonateValidUser(adConnectionUsername, adConnectionDomain, adConnectionPassword))
{
DirectoryEntry deUser = new DirectoryEntry(result.Path);
deUser.Properties["comment"].Value = comment;
deUser.CommitChanges();
deUser.Close();
UndoImpersonation();
}

Categories