I am writing a C# program to be pushed out the labs I work in. The program is to create a local admin account(itadmin), set the password, set the password to never expire, and add the account to the local Administrators group. The program creates the new user account and sets everything correctly but when it attempts to add it to the admin group I get a very nondescript exception. Do I have the add to group correct in the first place? What am I missing?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
namespace CreateITAdmin
{
class Program
{
static void Main(string[] args)
{
try
{
string userName = "itadmin";
string userPassword = "password";
Console.WriteLine("Building System Information");
DirectoryEntry localMachine = new DirectoryEntry("WinNT://.,computer");
DirectoryEntry newUser = localMachine.Children.Add(userName, "user");
DirectoryEntry admGroup = new DirectoryEntry("WinNT://./Administrators,group");
Console.WriteLine("Building User Information");
newUser.Properties["FullName"].Value = "IT Administrative User";
newUser.Invoke("Put", new object[] { "UserFlags", 0x10000 });
Console.WriteLine("Setting User Password");
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
Console.WriteLine("Adding itadmin to Administrators Group");
admGroup.Invoke("Add", "WinNT://./" + newUser);
Console.WriteLine("Cleaning Up");
localMachine.Close();
newUser.Close();
admGroup.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
Console.WriteLine(E.Message.ToString());
Console.ReadLine();
}
catch (System.Runtime.InteropServices.COMException E)
{
Console.WriteLine(E.Message.ToString());
Console.ReadLine();
}
catch (System.Reflection.TargetInvocationException E)
{
Console.WriteLine(E.Message.ToString());
Console.ReadLine();
}
catch (Exception E)
{
Console.WriteLine(E.Message.ToString());
Console.ReadLine();
}
Console.WriteLine();
Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
return;
}
}
}
The code output is below:
Building System Information
Building User Information
Setting User Password
Adding itadmin to Administrators Group
Exception has been thrown by the target of an invocation.
Any insight would be greatly appriciated.
UPDATE 1:
With the help of #Grumbler85 the exceptionis listed below:
System.Reflection.TargetInvocationException: Exception has been thrown by the target
of an invocation. ---> System.Runtime.InteropServices.COMException: A member could not
be added to or removed from the local group because the member does not exist. --- End
of inner exception stacktrace --- at System.DirectoryServices.DirectoryEntry.Invoke
(String methodName,Object[]args) at CreateITAdmin.Program.Main(String[]args)in
H:\code\CS\CreateITAdmin\CreateITAdmin\Program.cs:line 37
Also with the help of #Grumbler85 I have been working on updating the library use to System.DirectoryServices.AccountManagement. It seems to be a lot easier and a lot more straight forward in use. More updates/details to come as I progress.
Update 2:
I know this is a quick follow up but I was able to complete the update to the new namespace. After a minor hiccup with defining the machine, I was able to successfully create a user, set the password, update the password to never expire, and add the user to the administrators group. Thanks to #Grumbler85 for the update to the new namespace. The new code is below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
namespace CreateITAdmin
{
class Program
{
static void Main(string[] args)
{
string userName = "itadmin";
string userPassword = "IT-Engineering1";
PrincipalContext systemContext = null;
try
{
Console.WriteLine("Building System Information");
systemContext = new PrincipalContext(ContextType.Machine, null);
}
catch (Exception E)
{
Console.WriteLine("Failed to create System Context.");
Console.WriteLine("Exception: " + E);
Console.WriteLine();
Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
return;
}
//Check if user object already exists
Console.WriteLine("Checking if User Exists.");
UserPrincipal usr = UserPrincipal.FindByIdentity(systemContext, userName);
if (usr != null)
{
Console.WriteLine(userName + " already exists. Exiting!!");
Console.ReadLine();
return;
}
//Create the new UserPrincipal object
Console.WriteLine("Building User Information");
UserPrincipal userPrincipal = new UserPrincipal(systemContext);
userPrincipal.Name = userName;
userPrincipal.DisplayName = "IT Administrative User";
userPrincipal.PasswordNeverExpires = true;
userPrincipal.SetPassword(userPassword);
userPrincipal.Enabled = true;
try
{
Console.WriteLine("Creating New User");
userPrincipal.Save();
}
catch (Exception E)
{
Console.WriteLine("Failed to create user.");
Console.WriteLine("Exception: " + E);
Console.WriteLine();
Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
return;
}
GroupPrincipal groupPrincipal = null;
try
{
groupPrincipal = GroupPrincipal.FindByIdentity(systemContext, "Administrators");
if (groupPrincipal != null)
{
//check if user is a member
Console.WriteLine("Checking if itadmin is part of Administrators Group");
if (groupPrincipal.Members.Contains(systemContext, IdentityType.SamAccountName, userName))
{
Console.WriteLine("Administrators already contains " + userName);
return;
}
//Adding the user to the group
Console.WriteLine("Adding itadmin to Administrators Group");
groupPrincipal.Members.Add(userPrincipal);
groupPrincipal.Save();
return;
}
else
{
Console.WriteLine("Could not find the group Administrators");
}
}
catch (Exception E)
{
Console.WriteLine("Exception adding user to group.");
Console.WriteLine("Exception: " + E);
Console.WriteLine();
Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
}
Console.WriteLine("Cleaning Up");
groupPrincipal.Dispose();
userPrincipal.Dispose();
systemContext.Dispose();
Console.WriteLine();
Console.WriteLine("Press Any Key to Continue");
Console.ReadLine();
return;
}
}
}
For Update 3 (for Multi Language support)
Please use build in identifiers --> "Well Known SIDs" for build in accounts or groups:
var sAdministrators = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid , null).Translate(typeof(NTAccount)).Value;
groupPrincipal = GroupPrincipal.FindByIdentity(systemContext, IdentityType.Name, sAdministrators.ToString());
and not: ..... FindByIdentity(systemContext, "Administrators");
Because if you want to use it "world wide" and outside of the engl. world you will get an error. Example: Germany use "VORDEFINIERT\Administratoren" as Name.
You mention that these machines are on a domain, it is much simpler to just do this with group policy.
Go in to group policy management (gpmc.msc) and create a new policy. Once you have a new policy created go to Computer Configuration->Prefrences->Local Users and Groups.
From there right click and go to New->Local User. In the new screen set the action to Create (you can click the help button to see the difference between the modes) and enter your info for the user in that screen.
One you click ok the user will show up on the screen on the local users and groups page. From there right click and go to New->Local Group. On the new page set the action to Update, use the drop-down to find the group name Administrators (built-in) and select it. In the bottom section click Add... and type in by hand the same name you put in from the previous screen (itadmin in your case). At the end it should look like this
the Local Users and Groups page will look like this
It is important to notice the Order column, the update on the administrator's group must have a higher order number than the user creation command.
One you have your group policy set up apply the policy to the machines that are in the lab (be it through OU targeting or Security Filtering, or WMI Filtering). On next reboot the local itadmin user will be created on each machine.
Also a interesting note, when you choose the user when selecting who to add to the local administrators group, you can click the ... and choose a user on the domain this will allow someone to use their domain login to be a local admin on a small set of computers without giving them rights to be a admin everywhere. However they will need to be able to log in using the domain for this to work, so if you are troubleshooting a network connectivity issue your current approach may be a better thing to do.
Related
I have two sites, each has its own Scheduler with enough rights to do their job. We created an application to allow them to quickly add a user to AD with every thing the user will need. The problem is that sometimes, a student will transfer from one site to the other temporarily. While at the new site, they will need access to all their original stuff, plus access to the new sites stuff as well.
I had a request to add a button that will quickly allow them to add the other sites groups and not remove the original sites groups.
The error I am getting is:
Error adding User to Group.
System.Directory.Services.AccountManagement.NoMatchingPrincipalException:
No principal matching the specified paramenters was found
at
System.DirectoryServices.AccountManagement.principalCollection.add(principalContext context, identitytype itedentity type, string identitiyvalue)
at AddStudentUser.Form2.AddUserToGroup_fm2(string userID, string groupName, string siteName) identitiyvalue
c:\projects\AddStudentUser\AddStudentUser\Form2.cs:line 113
Here is the code I am calling:
private void btnUpdateExit_Click(object sender, EventArgs e)
{
//userUPN will equal Loginname#domain.com
string userUPN = this.tbUserLoginName.Text.Trim().ToString() + Form1.Globs.strUPN;
if (this.cbSite1.Checked == true & this.cbSite1.Enabled==true)
{
AddUserToGroup_fm2(userUPN, "Site1", Form1.Globs.strSUUADC);
AddUserToGroup_fm2(userUPN, "Crew_Site1", Form1.Globs.strSite1ADC);
AddUserToGroup_fm2(userUPN, "WWW-Site1", Form1.Globs.strSite1ADC);
MessageBox.Show("User has been added to the Site1 Groups.");
}
if (this.cbSite2.Checked == true & this.cbSite2.Enabled == true)
{
AddUserToGroup_fm2(userUPN, "Site2", Form1.Globs.strWRIADC);
AddUserToGroup_fm2(userUPN, "Crew_Site2", Form1.Globs.strSite2ADC);
AddUserToGroup_fm2(userUPN, "WWW-Site2", Form1.Globs.strSite2ADC);
MessageBox.Show("User has been added to the Site2 Groups.");
}
this.Close();
}
public void AddUserToGroup_fm2(string userId, string groupName, string siteName)
{
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, siteName))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
group.Members.Add(pc, IdentityType.UserPrincipalName, userId);
group.Save();
}
}
catch (Exception E)
{
MessageBox.Show("Error adding User to Group. " + E);
}
}
The weird thing is that if I add a new user, the same code on a different form works without issue. I initially tried calling that code but was getting the same error as I am getting now. I wanted to separate the code so that I could make changes to it without affecting the original function, since it works fine when adding a new user to existing groups.
Any assistance is appreciated.
IdentityType.SamAccountName
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, siteName))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
group.Members.Add(pc, IdentityType.SamAccountName, userId);
group.Save();
}
Im writtem app that looks for a user in active directory based on there name
When I try to create new PrincipalContext with ContextType.Domain and the domain name as a string, I get the exception "The server could not be contacted."
So to get the the AD that I did this ...
Open System by clicking the Start button Picture of the Start button, clicking Control Panel, clicking System and Maintenance, and then clicking System.
Which I got from http://windows.microsoft.com/en-gb/windows-vista/find-the-domain-your-computer-is-on
Which gave me
Something.Something (not this, but two strings with a . between them)
So when I run the following code and enter Something.Something as the domain, I get the exception "The server could not be contacted." on the new PrincipalContext(ContextType.Domain, domain);
I have tried changing the case of the string and the but dont seem to have any luck.
So what should I be using for the domain?
public static void Main(string[] args)
{
try
{
Console.WriteLine("Enter Domain, then press enter.");
string domain = Console.ReadLine();
Console.WriteLine("Enter First Name, then press enter.");
string userName = Console.ReadLine();
//This is the line that always crashes throws error
var principalContext = new PrincipalContext(ContextType.Domain, domain);
var user = UserPrincipal.FindByIdentity(principalContext, userName);
if (user == null)
{
Console.WriteLine("User Not Found");
}
var groups = user.GetGroups(principalContext);
var result = new List<string>();
groups.ToList().ForEach(sr => result.Add(sr.SamAccountName));
foreach (var item in result)
{
Console.WriteLine(item);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Ashley is correct... assuming you are running the application on a machine that is joined to the domain.
using System;
using System.DirectoryServices.AccountManagement;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter First Name, then press enter.");
var userName = Console.ReadLine();
// Will search the domain the application is running on
var principalContext = new PrincipalContext(ContextType.Domain);
var user = UserPrincipal.FindByIdentity(principalContext, userName);
if (user == null)
{
Console.WriteLine("User Not Found");
}
else
{
// Gets a list of the user's groups
var groups = user.GetGroups().ToList();
// Loops the groups and prints the SamAccountName
groups.ForEach(g => Console.WriteLine(g.SamAccountName));
}
Console.ReadKey();
}
}
}
If you've got several seconds to spare waiting for your data form a large AD, then go ahead and use PrincipalContext but if you want your response in milliseconds, use DirectoryEntry, DirectorySearcher and .PropertiesToLoad.
Here's an example for getting the groups for a user:
https://stackoverflow.com/a/65986796/5248400
Is there a way to retrieve the users active directory information like ad groups, SID and so on?
Currently I could find only examples with active directory azure.
At the moment I catch user information the following way:
IReadOnlyList<User> users = await User.FindAllAsync(UserType.LocalUser);
foreach (User user in users)
{
string displayName = (string)await user.GetPropertyAsync(KnownUserProperties.DisplayName);
}
Also the Win10 SDK doesn't provide any examples regarding active directory.
[Edit] There is a WinRT Api request on https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/12556779-get-active-directory-user-information
[Edit] Regarding Issue on corefx https://github.com/dotnet/corefx/issues/7325
From command line you can use whoami /groups.
From C# you can check membership in a group using the SID using something like this:
using System;
using System.Security.Principal;
namespace ADMembership
{
class Program
{
static void Main()
{
WindowsPrincipal myPrincipal = new
WindowsPrincipal(WindowsIdentity.GetCurrent());
String groupName = "A Group";
SecurityIdentifier sid = new SecurityIdentifier(
"S-1-1-22-999999999-999999999-999999999-4444");
if (myPrincipal.IsInRole(groupName))
{
Console.WriteLine("{0} is in {1}.",
myPrincipal.Identity.Name.ToString(), groupName);
}
else
{
Console.WriteLine("{0} is not in {1}.",
myPrincipal.Identity.Name.ToString(), groupName);
}
if (myPrincipal.IsInRole(sid))
{
Console.WriteLine("{0} has SID: {1}.",
myPrincipal.Identity.Name.ToString(), sid);
}
else
{
Console.WriteLine("{0} does not have SID {1}.",
myPrincipal.Identity.Name.ToString(), sid);
}
}
}
}
I've been trying to create new local user accounts on windows 7 machine. I used the System.DirectoryServices.DirectoryEntry class (as in here) but it doesn't seem to work.
Here's the code in the article:
static void Main(string[] args)
{
try
{
DirectoryEntry AD = new DirectoryEntry("WinNT://" +
Environment.MachineName + ",computer");
DirectoryEntry NewUser = AD.Children.Add("TestUser1", "user");
NewUser.Invoke("SetPassword", new object[] {"#12345Abc"});
NewUser.Invoke("Put", new object[] {"Description", "Test User from .NET"});
NewUser.CommitChanges();
DirectoryEntry grp;
grp = AD.Children.Find("Guests", "group");
if (grp != null) {grp.Invoke("Add", new object[] {NewUser.Path.ToString()});}
Console.WriteLine("Account Created Successfully");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
When executing this line
DirectoryEntry NewUser = AD.Children.Add("TestUser1", "user");
I get a
System.Runtime.InteropServices.COMException with "{"Unknown error (0x80005000)"}"
as the exception message, and -2147463168 as the error code.
I assume this is probably because the article targets Windows XP and below machines, and I'm targeting windows 7 and Windows server 2008.
Any help appreciated!
Update:
For some mysterious reason, i'm no longer seeing that System.Runtime.InteropServices.COMException, however, when committing the changes here newuser.CommitChanges(), I get a "UnAuthorizedAccessException". I tried running the app as administrator, but still not working.
Update 2:
OK, after changing to the UserPrincipal class, i got the follwoing code to work:
public UserPrincipal CreateNewUser(string sUserName, string sPassword)
{
// first check that the user doesn't exist
if (GetUser(sUserName) == null)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = new UserPrincipal(oPrincipalContext);
oUserPrincipal.Name = sUserName;
oUserPrincipal.SetPassword(sPassword);
//User Log on Name
//oUserPrincipal.UserPrincipalName = sUserName;
oUserPrincipal.Save();
return oUserPrincipal;
}
// if it already exists, return the old user
return GetUser(sUserName);
}
}
This code runs well when I run it as a console app -of course run as administrator- but when i deployed it as a windows service, with the security account set as "LocalSystem", i get an InvlaidOperationException saying "The underlying store does not support this property"
Thoughts?
OK, if you check my last update, the following snippet worked:
public UserPrincipal CreateNewUser(string sUserName, string sPassword)
{
// first check that the user doesn't exist
if (GetUser(sUserName) == null)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = new UserPrincipal(oPrincipalContext);
oUserPrincipal.Name = sUserName;
oUserPrincipal.SetPassword(sPassword);
//User Log on Name
//oUserPrincipal.UserPrincipalName = sUserName;
oUserPrincipal.Save();
return oUserPrincipal;
}
// if it already exists, return the old user
return GetUser(sUserName);
}
}
That worked as a console app, but failed to execute due to security exceptions when deployed as a windows service. A solution is to trust that assembly (the windows service assembly) so that the .net security will let it run. That's done, now everything is cool!
You need to prefix your username with CN=, like so:
DirectoryEntry NewUser = AD.Children.Add("CN=TestUser1", "user");
I am using a WCF service to expose certain Active Directory management functions to our help desk staff without giving them the group membership required to manipulate AD directly. Adding users to and removing users from groups is working like a champ with existing users, but every time I create a new user it throws back this fun code:
The server is unwilling to process the request. (Exception from HRESULT: 0x80072035)
The code I use to add the user to the group is
public bool AddGroupToUser(string userDn, string groupDn)
{
try
{
DirectoryEntry groupEntry = LdapTools.GetDirectoryEntry(groupDn);
groupEntry.Properties["member"].Add(userDn);
groupEntry.CommitChanges();
groupEntry.Close();
return true;
}
catch (DirectoryServicesCOMException)
{
return false;
}
}
Everything I've read on the subject is rather vague and I can't seem to find out WHY the exception is being triggered. Any ideas?
UPDATE
This is the code I use to create the user in AD:
try
{
DirectoryEntry container = GetDirectoryEntry(storageOu);
DirectoryEntry newUser = container.Children.Add("CN=" + employee.FullName, "user");
newUser.Properties["sAMAccountName"].Value = employee.Username;
newUser.Properties["displayName"].Value = employee.FullName;
newUser.Properties["givenName"].Value = employee.FirstName;
newUser.Properties["sn"].Value = employee.LastName;
newUser.Properties["department"].Value = departmentName;
newUser.Properties["userPrincipalName"].Value = employee.Username + "#APEX.Local";
newUser.CommitChanges();
newUser.Invoke("SetPassword", new object[] { employee.Password });
newUser.CommitChanges();
AdsUserFlags userSettings = AdsUserFlags.NormalAccount;
newUser.Properties["userAccountControl"].Value = userSettings;
newUser.CommitChanges();
ldapPath = newUser.Path;
newUser.Close();
container.Close();
}
catch (DirectoryServicesCOMException e)
{
// Something went wrong... what???
}
catch (Exception e)
{
// Something else went wrong
}
The new user can login, and can be manipulated using the standard MS tools.
Apparently, unless I'm missing an important step here, the issue is time. When forcing the system to sleep for 8 seconds before attempting to add groups to the new user the process works. If I do it any sooner than the 8 second mark it fails.
I'm marking this answer as correct unless anybody can provide me a better solution.
Try:
public bool AddUserToGroup(string userName, string groupName)
{
bool done = false;
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName);
if (group == null)
{
group = new GroupPrincipal(context, groupName);
}
UserPrincipal user = UserPrincipal.FindByIdentity(context, userName);
if (user != null & group != null)
{
group.Members.Add(user);
group.Save();
done = (user.IsMemberOf(group));
}
return done;
}
Reference:
http://www.c-sharpcorner.com/UploadFile/dhananjaycoder/activedirectoryoperations11132009113015AM/activedirectoryoperations.aspx
As it mentioned in here, can you tell us that you set the password of the newly created users? In the reference, it says that you should SetPassword of the user before doing anything with it.
Time issue could occur from a Active Directory replication issue.
Once, I gave a product to my customer which creates user over Active Directory, which has over 10.000 records in it, with the given informations on a SharePoint form and then program adds the user to a SharePoint group. The problem was, SharePoint throws an error about the newly created user, it says user does not exist in AD.
So, one of out system engineer told us about the replication opearation on the Active Directory that it could be the source of the problem and it was true.
For the solution, program tries to do the job for 10 times with 1 second sleeps. No problem occurred so far. So as a solution, I would suggest you to check out the AD for the existence of replication method.
P.S: When I asked questions to my customers system engineers about the replication operation, they all rejected the existence of AD replication operation and told me that the program has the problem. They believed me when we created a new user in AD from a computer, we couldn't see the user for 5 seconds on an another computer.