Create Windows User programmatically c# .net (using PricinpalUser / CreateProfile) - c#

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.

Related

C# Error when adding existing user to existing group

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();
}

Exception Details: System.Security.SecurityException: A specified logon session does not exist. It may already have been terminated

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;
}
}
}
}

Create New Windows 8 / 8.1 User with Windows Store App

Good morning, Using C# we are trying to create a new windows 8 / 8.1 user on windows store application.
But the code using below is not working, because the namespace "System.DirectoryServices.AccountManagement" is not available.
public UserPrincipal CreateNewUser(string a_userName, string sPassword)
{
if (!UserExists(a_userName))
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = new UserPrincipal(oPrincipalContext);
oUserPrincipal.Name = a_userName;
oUserPrincipal.SetPassword(sPassword);
//User Log on Name
oUserPrincipal.UserPrincipalName = a_userName;
oUserPrincipal.Save();
return oUserPrincipal;
}
// if it already exists, return null
return null;
}
private PrincipalContext GetPrincipalContext()
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine);
return oPrincipalContext;
}
private bool UserExists(string a_userName)
{
using (var pc = new PrincipalContext(ContextType.Machine))
{
using (var p = Principal.FindByIdentity(pc, IdentityType.SamAccountName, a_userName))
{
return p != null;
}
}
}
We donĀ“t know how to find the way to create a windows user (With or without password it doesnt matter) because all the namespaces necessaries for that are not available on Windows Store Application project.
If we try to import some dll the error is:
"A reference to "[Dll path]" could not be added. The project targets '.Net Core' while the file targets '.NetFramework'. This is no a supported sceneraio"
Is there any possible solution?
This is not possible, and shouldn't be. Windows Store apps don't have the rights to create users because it would be a huge huge security risk! Why are you even trying to do this?

How to programmatically create Windows user accounts on Windows 7 or Windows Server 2008?

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");

System.DirectoryServices.DirectoryServicesCOMException (0x8007203B): A local error has occurred

When we try to search for a user in ActiveDirectory, we get that exception - 0x8007203B.
Basically we deployed a web service, which uses DirectoryEntry & DirectorySearcher class to find a user in AD, and sometimes this exception happens. But when we do IISReset, it again works fine.
Code is very simple like this:
DirectoryEntry domainUser = new DirectoryEntry("LDAP://xxx.yyy/dc=xxx,dc=yyy", "domain\user", "pwd", AuthenticationTypes.Secure);
DirectoryEntry result = new DirectorySearcher(domainUser, filter);
Only some times this happens. I don't have much information to provide, any guess much appreciated
This is how my filter looks like
public static string BuildFilter(DirectoryEntry dirEntry, string userName, string userMail)
{
try
{
string filter = string.Empty;
if (!string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(userMail))
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(|(CN={0})(samaccountname={0})))", userName);
else if (string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(userMail))
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(mail={0}))", userMail);
else
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(|(CN={0})(samaccountname={0})(mail={1})))", userName, userMail);
return filter;
}
catch (Exception ex)
{
_logger.Error("BuildUserSearch - Failed to build LDAP search", ex);
}
return null;
}
You say that this it's just append after some time. As DirectoryEntry and DirectorySearcher are built on COM object into disposable class I would first just add some using sections to be sure that underlying objects are corectly freed.
using(DirectoryEntry root = new DirectoryEntry(ldapPath))
{
using(DirectorySearcher searcher=new DirectorySearcher(root))
{
...
}
...
}
Any guess are appreciated?
Then here's mine:
ASP.NET: DirectoryServicesCOMException [...];
Windows Error Codes: Repair 0x8007203B. How To Repair 0x8007203B.
What makes me confuse is that you say it works most of the time...
Did this help?
P.S. I'll update if I think of anything else.

Categories