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();
}
Related
I deployed a test web app to virtual Windows server 2008 std. IIS does not have certificates features assigned to this app nor to any apps deployed on this server so none of the solutions I've found relate to my issue. All other apps on same server work fine which leads me to conclusion the problem must be with the code I am using to authenticate in the global.asax file.
I've checked gpedit.msc and Network access: Do not allow storage of credentials is already disabled. This posting is closest to my issue that I've been able to find but no solution was accepted. I've checked MMC but there is nothing in it but an empty Console Root node so there is nothing to delete and reinstall as some have suggested here and here. I cannot access blog sites from work-there were some that sounded promising but I can't read them. I added Full Trust to web.config it made no difference and noted that .NET Trust Levels in IIS were set at Full (internal) already.
The complete error message is:
System.Security.SecurityException: A specified logon session does not exist. It may already have been terminated.
at System.Security.Principal.WindowsIdentity.KerbS4ULogon(String upn)
at System.Security.Principal.WindowsIdentity..ctor(String sUserPrincipalName, String type)
at System.Security.Principal.WindowsIdentity..ctor(String sUserPrincipalName)
at EPRSystem.Global.IsInADGroup(String user, String group)
at EPRSystem.Global.Application_AuthenticateRequest(Object sender, EventArgs e)
The Zone of the assembly that failed was:
MyComputer
Any ideas for me?
Here's my global code:
public Boolean IsAdmin;
public Boolean IsManager;
public Boolean IsDeveloper;
string UserName;
public String GetUserName()
{
WindowsIdentity wiCurrentUser;
wiCurrentUser = WindowsIdentity.GetCurrent();
String strUserName = wiCurrentUser.Name;
String[] strParts = strUserName.Split('\\');
strUserName = strParts[1];
return strUserName;
}
public Boolean IsInADGroup(string user, string group)
{
using (var identity = new WindowsIdentity(user))
{
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(group);
}
}
protected void Session_Start(object sender, EventArgs e)
{
//Write method: Get current user's username
UserName = HttpContext.Current.User.Identity.Name; //get AD name of user
HttpContext.Current.Session["UserName"] = GetUserName();
HttpContext.Current.Session["IsAdmin"] = IsInADGroup(HttpContext.Current.Session["UserName"].ToString(), "group1");
HttpContext.Current.Session["IsManager"] = IsInADGroup(HttpContext.Current.Session["UserName"].ToString(), "group2");
HttpContext.Current.Session["IsDeveloper"] = IsInADGroup(HttpContext.Current.Session["UserName"].ToString(), "group3");
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
//Write method: Identity/Authenticate current user
DAL.ErrorLog oErrorLog = new DAL.ErrorLog();
try
{
String strUser = GetUserName();
IsAdmin = IsInADGroup(strUser, "group1");
IsManager = IsInADGroup(strUser, "group2");
IsDeveloper = IsInADGroup(strUser, "group3");
}
catch (System.Security.SecurityException ex)
{
oErrorLog.WriteErrorLog(ex.ToString());
}
}
I read this article by Shawn Farkas, focusing on his comment "1.Determine what permissions are being demanded that are causing your application to throw, and try to modify your application to not require these permissions any more. The SecurityException that is being thrown should tell you which demand
failed.
I removed the authorization code from Global.asax entirely, moving it to Default.aspx.cs. I replaced IsInADGroup(x,y) method which is where the error originated, with a mixture of code suggested by marc_s in a new method called CheckGroupMembership(). I instantiated a global array variable groupName[] containing the three AD groups I wanted to check membership for and ultimately these values IsMember[]are passed to Session variable so they can be used on another page. The heart of the solution is this method: requires namespace System.DirectoryServices.AccountManagement
public void CheckGroupMembership()
{
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "XXX");
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, GetUserName());
for (int i = 0; i < 3; i++)
{
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, groupName[i]);
if (user != null)
{
// check if user is member of that group
if (user.IsMemberOf(group))
{
IsMember[i] = true;
}
else
{
IsMember[i] = false;
}
}
}
}
I've created local windows account and added it to group Users using classes from System.DirectoryServices.AccountManagement namespace. Then I tried to log into that newly created account (manually not from C#) and got error 'user profile service failed user profile cannot be loaded'. My C# code is:
try
{
// Create context for server and create new user if one not exists
string userID = GetUserID(userName, userType);
PrincipalContext serverContext = GetDefaultPrincipalContext();
if (DoesExist(userID, serverContext))
{
throw new Exception("User already exists!");
}
UserPrincipal newUser = new UserPrincipal(serverContext, userID, userID, true);
// Get description of user from its privilege and save user if everything went OK
var description = GetDescriptionFromUserType(userType);
if (null != description)
{
newUser.Description = description;
newUser.Save();
// Add user to group so it is displayed in Control Panel
GroupPrincipal group = GroupPrincipal.FindByIdentity(serverContext, USERS_GROUP);
group.Members.Add(newUser);
group.Save();
group.Dispose();
newUser.Dispose();
serverContext.Dispose();
success = true;
}
}
catch (Exception e)
{
Log.LogError(TYPE, e.Message);
DeleteUser(userName, userType);
}
What i'm missing?
I've forgot to write, GetDefaultPrincipalContext() returns new PrincipalContext(Context.Machine, null)...
UPDATE:
Just to mention that I haven't made home directory and all the stuff inside it for new user on path C:\Users\NewUser... And what is strange (in my opinion), one isn't made automatically.
I'm writing an answer in case that anyone sees this post and thinks that the code above is no good. Actually code is working nicely on every other windows machine on which I tried to run it. I don't know what is the issue on my personal computer but the problem is definitely not in code... I will ask other question to resolve my windows problem...
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.
In a nutshell, what I'm trying to do is create a new user, which has the ability to log in.
I have plucked code from various sources, and tried to simplify it. However, I'm hitting a few stumbling blocks.
When I call UserPrincipal.Save() - it gives me an error
'The directory property cannot be found in the cache' with an
exception type of.. 'COMExceptioncrossed a native/managed boundary'.
For some reason, when I run my program directly (not through vs2010) it works fine. So I can get around that !
My main problem though, is that even though everything seems ok, when I try to log in, it comes up with the message 'loading desktop' or whatever it is, and then just says 'logging out'. So it's almost as if the profile hasn't been set up correctly.
The return value from the API 'CreateProfile' isn't 0, so maybe that's causing a problem.
Is there anything else I need to do ?
My Code is...
private void Run(string un, string pw)
{
UserPrincipal NewUP = CreateUser(un, pw);
AddGroup(NewUP, "Users");
AddGroup(NewUP, "HomeUsers");
CreateProfile(NewUP);
}
private UserPrincipal CreateUser(string Username, string Password)
{
PrincipalContext pc = new PrincipalContext(ContextType.Machine, Environment.MachineName);
UserPrincipal up = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, Username);
if (up == null)
{
up = new UserPrincipal(pc, Username, Password, true);
up.UserCannotChangePassword = false;
up.PasswordNeverExpires = false;
up.Save(); // this is where it crashes when I run through the debugger
}
return up;
}
private void AddGroup(UserPrincipal Up, string GroupName)
{
PrincipalContext pc = new PrincipalContext(ContextType.Machine, Environment.MachineName);
GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, GroupName);
if (!gp.Members.Contains(Up))
{
gp.Members.Add(Up);
gp.Save();
}
gp.Dispose();
}
private void CreateProfile(UserPrincipal Up)
{
int MaxPath = 240;
StringBuilder pathBuf = new StringBuilder(MaxPath);
uint pathLen = (uint)pathBuf.Capacity;
int Res = CreateProfile(Up.Sid.ToString(), Up.SamAccountName, pathBuf, pathLen);
}
Strangely, when this is run on a server machine (i.e. not my development machine) it works fine. I've got a feeling this is something to do with Windows 7, or my particular installation of it.
Thanks for your suggestions anyway.
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.