Change AD user Terminal Server properties with C# - c#

I am using System.DirectoryServices.DirectoryEntry to create AD user and everything work fine except for some Remote Desktop specifics properties.
Exemple :
newUser.Properties["msTSConnectClientDrives"].Value = false;
newUser.Properties["msTSConnectPrinterDrives"].Value = false;
newUser.Properties["msTSDefaultToMainPrinter"].Value = false;
This doesn't throw any exception, so I guess the properties are found in the object but they don't have any effect. When I go into the property window of that user, under "Environment" tab, these 3 checkbox are still checked on.
Am I missing something particular for these properties ?
Thank for your help.
EDIT :
Sorry I have been really busy, here is a code sample :
private string CreateNewADAccount(string accountName, string accountPassword)
{
try
{
PrincipalContext context = new PrincipalContext(ContextType.Domain, "SV-LITE", #"LITE\xxxxxxxx", "yyyyyyyy");
UserPrincipal newUser = new UserPrincipal(context);
newUser.SamAccountName = accountName;
newUser.UserPrincipalName = accountName;
newUser.Name = "LiteUser2015 - " + accountName;
newUser.DisplayName = "LiteUser2015 - " + accountName;
newUser.SetPassword(accountPassword);
newUser.PasswordNeverExpires = true;
newUser.UserCannotChangePassword = true;
newUser.Save();
// Set advanced properties
if (newUser.GetUnderlyingObjectType() == typeof(DirectoryEntry))
{
DirectoryEntry entry = (DirectoryEntry)newUser.GetUnderlyingObject();
entry.Properties["msTSConnectClientDrives"].Value = false;
entry.Properties["msTSConnectPrinterDrives"].Value = false;
entry.Properties["msTSDefaultToMainPrinter"].Value = false;
entry.Properties["msTSInitialProgram"].Value = "test";
entry.CommitChanges();
}
return newUser.Guid.ToString();
}
catch (Exception e)
{
MessageBox.Show("Failed to create PrincipalContext. Exception: " + e);
}
return null;
}

After making the changes, you have to call CommitChanges - newUser.CommitChanges();
See https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.commitchanges%28v=vs.110%29.aspx
By default, changes to properties are made locally to a cache..

It might have something to do with the Server OS version you're using. I found this answer on another site that talks about Windows 2000 and 2003. It should work for Windows2008 and above:
For 2000 / 2003 you have to access them using the Terminal Services
ADSI extension. The reference for that is here:
http://msdn.microsoft.com/en-us/library/aa380823(VS.85).aspx

Related

Login Validation Against Active Directory C# - Visual Studio for Mac

I'm trying to validate credentials in Active Directory for an MVC Web App (C#) I'm writing on Visual Studio for Mac. In searching for answers, I've noticed a lot of NotImplementedExceptions and other strange occurrences.
Here is a (non-comprehensive) list of things I've tried and things that have failed.
First, this code:
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
//Bind to the native AdsObject to force authentication.
// This line throws a not implemented exception
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(SAMAccountName=" + username + ")"
};
search.PropertiesToLoad.Add("cn");
Console.WriteLine(search.ToString());
SearchResult result = search.FindOne();
//Debug.WriteLine(result.ToString());
if (null == result)
{
return false;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
The line object obj = entry.NativeObject throws a NotImplementedException I've tried commenting out the line (since obj is never used elsewhere) but to no avail. I have tried other variations of very similar code as well.
The other route I attempted was this code:
var creds = new NetworkCredential(username, password);
var srvid = new LdapDirectoryIdentifier(adPath);
// This line throws a NotImplementedException
var conn = new LdapConnection(srvid, creds);
try
{
conn.Bind();
}
catch (Exception)
{
return false;
}
conn.Dispose();
return true;
And other variations of the same idea. The line var conn = new LdapConnection(srvid, creds); throws a NotImplementedException
Finally, I went a simpler route and used
Membership.ValidateUser(model.username, model.password)
Since this is a Web Api and I am using a controller. This requires some code in the Web.config available here. It, too, throws a NotImplementedException.
So do these three common methods all rely on the same underlying function that hasn't been implemented in VS for Mac yet? Or is there something I'm missing? Also if there is any workaround someone could offer, it would be very well appreciated.
Thanks!
First of all you need to reference Microsoft.DirectoryServices.dll
System.DirectoryServices
Second, you need to run this queries with a domain account with grant access. Take care when you publish in server (iis) or other because the service run this code and it had to have permission on it.
check this
Sorry for my English :D
Thanks to the kind efforts of Javier Jimenez Matilla, bnem, and Lexi Li, I got it to work. Part of the issue was a configuration problem that I unfortunately can't disclose (confidentiality and whatnot), but here's the final code:
using Novell.Directory.Ldap;
try
{
var conn = new LdapConnection();
conn.Connect(domain, 389);
conn.Bind(LdapConnection.Ldap_V3, $"{subDomain}\\{username}", password);
return true;
}
catch (LdapException ex)
{
Console.WriteLine(ex.Message);
return false;
}
A few things to note. First, that LdapConnection is actually Novell.Directory.Ldap.LdapConnection. Secondly, the hard-coded 389 is the port which LDAP likes to communicate over. Thirdly, the parameter $"{subDomain}\\{username}" is the way the username appears to AD. For my particular case, the domain is of the form "corporation.mycompany.com", and in AD the username appears corporation\username. So in this example, string subDomain = "corporation"

Active Directory move a user to a different OU

I'm working on a program that will automate the separation process for users leaving our network. One of the tasks it performs is moving the user account from the OU it is in, to a Former Employees OU. I've been having problems with this step even though I've not had any issues doing other processes with DirectoryServices. Here's my code thus far (note: I know I need to stop catching and eating all exceptions. This will be addressed and corrected before release. Any advice on which exceptions I should catch and which I should not would be appreciated too):
private const string AD_DOMAIN_NAME = "domain.com";
private const string AD_NEW_PASSWORD = "TestPassword123";
private const string AD_FORMER_EMPLOYEES_OU = "LDAP://OU=Former Employees,DC=domain,DC=com";
static DirectoryEntry CreateDirectoryEntry(string connectionPath,
string adUserName, string adPassword)
{
DirectoryEntry ldapConnection = null;
try
{
ldapConnection = new DirectoryEntry(AD_DOMAIN_NAME, adUserName, adPassword);
ldapConnection.Path = connectionPath;
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
}
catch (Exception ex)
{
MessageBox.Show("Exception Caught in createDirectoryEntry():\n\n" + ex.ToString());
}
return ldapConnection;
}
private void btnProcessSeparation_Click(object sender, EventArgs e)
{
if (cboOffice.SelectedItem != null && lstUsers.SelectedItem != null)
{
string userOU = cboOffice.SelectedItem.ToString();
string userName = lstUsers.SelectedItem.ToString();
string userDn = "LDAP://OU=" + userOU + ",OU=Employees,DC=domain,DC=com";
using (DirectoryEntry ldapConnection = CreateDirectoryEntry(userDn))
{
using (DirectorySearcher searcher = CreateDirectorySearcher(ldapConnection,
SearchScope.OneLevel, "(samaccountname=" + userName + ")", "samaccountname"))
{
SearchResult result = searcher.FindOne();
if (result != null)
{
using (DirectoryEntry userEntry = result.GetDirectoryEntry())
{
if (userEntry != null)
{
using (DirectoryEntry formerEmployees = CreateDirectoryEntry(
AD_FORMER_EMPLOYEES_OU))
{
userEntry.MoveTo(formerEmployees); // This line throws an DirectoryServicesCOMException.
}
userEntry.CommitChanges();
userEntry.Close();
MessageBox.Show("Separation for {0} has completed successfully.", userName);
}
}
}
}
}
}
else
{
MessageBox.Show("Error, you did not select an OU or a user. Please try again.");
}
}
The above code works just fine until the userEntry.MoveTo(formerEmployees); line. That line throws a DirectoryServicesCOMException with the additional information saying An invalid dn syntax has been specified. It is strange because I'm using the same format as the other DirectoryEntry's that work just fine. I've added a break point and confirmed that formerEmployees is set to: LDAP://OU=Former Employees,DC=domain,DC=com. I copied everything after LDAP:// directly from the OU's distinguishedName attribute in Active Directory to make sure it was correct.
Is the space in the OU name causing the problem? I got this to work once just fine and moved on to the other tasks and must have changed something that broke this. I've been looking at the code too much I think and just can't seem to see why it thinks I'm sending an invalid dn.
Thanks for any and all help!
Hope this helps:
DirectoryEntry eLocation = Conexion.Conectar(Localitation);
DirectoryEntry nLocation =Conexion.Conectar(NewLocalitation);
string newName = eLocation.Name;
eLocation.MoveTo(nLocation, newName);
nLocation.Close();
eLocation.Close();
After #David pointed me in the right direction of making sure I had the correct permissions to the OU, I discovered the problem. I added an overloaded CreateDirectoryEntry method that uses the username and password (which is what I put in the code above). However, if you notice in the code above, I call the method that only takes the connection path.
Thanks for the help #David!

WMI Win32_Share.Create method on localhost via WMI on Server OSes gives Error Code 24

I am detecting whether or not I'm attempting a connection against localhost, and creating (or not) the WMI connection options as follows:
if (NetworkUtils.IsLocalIpAddress(machineName))
{
_scope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", machineName));
}
else
{
_connectionOptions = new ConnectionOptions
{
Username = username,
Password = password,
Impersonation = ImpersonationLevel.Impersonate
};
_scope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", machineName), _connectionOptions);
}
When I call _scope.Connect() in either case, it works. That is, no exception and IsConnected is true.
However, when I attempt to invoke a method in the local case, such as Win32_Share.Create I get errors. The following code always works for remote connections for me:
var winSharePath = new ManagementPath("Win32_Share");
var winShareClass = new ManagementClass(_scope, winSharePath, null);
var shareParams = winShareClass.GetMethodParameters("Create");
shareParams["Path"] = pathName.TrimEnd('\\');
shareParams["Name"] = shareName;
shareParams["Type"] = 0;
shareParams["Description"] = "CMC Bootstrap Share";
var outParams = winShareClass.InvokeMethod("Create", shareParams, null);
if ((uint) (outParams.Properties["ReturnValue"].Value) != 0)
{
throw new Exception("Unable to share directory. Error code: " +
outParams.Properties["ReturnValue"].Value);
}
I create the pathName directory just prior to invoking this method, so I guarantee pathName exists in all cases.
When executing locally ONLY on Windows Server 2008 & 2012, the above code throws the exception with error code 24. Executing against localhost on Windows 8 works just fine.
What is the correct way to specify "blank credentials" when invoking WMI methods against localhost, as I believe this is the underlying issue?
I tried the code below on my local PC and this works (shares my temp folder). Could you try the same please? Also, which is the patch & share name you're using?
string pathName = #"c:\temp\";
string shareName = "tempFolder";
var scope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", "localhost"));
// your code below
var winSharePath = new ManagementPath("Win32_Share");
var winShareClass = new ManagementClass(scope, winSharePath, null);
var shareParams = winShareClass.GetMethodParameters("Create");
shareParams["Path"] = pathName.TrimEnd('\\');
shareParams["Name"] = shareName;
shareParams["Type"] = 0;
shareParams["Description"] = "CMC Bootstrap Share";
var outParams = winShareClass.InvokeMethod("Create", shareParams, null);
if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
{
throw new Exception("Unable to share directory. Error code: " +
outParams.Properties["ReturnValue"].Value);
}
the above code throws the exception with error code 24
That doesn't have anything to do with the error you mention in the title of your question. Error codes for Win32_Share.Create method are documented in this MSDN article. Return value 24 means "Unknown Device or Directory".
In other words, your pathName variable is wrong.

how to delete computer account from Active Directory using c#

Is there any sample that deletes computer account from AD using C#?
I have searched many sources, but all are about user account.
added my code here, i always got errors for some reason.
public static bool checkExistingPC(string compName,string userName,string userPwd )
{
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://test.com",userName,userPwd,AuthenticationTypes.Secure);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=computer)(|(cn=" + compName + ")(dn=" + compName + ")))";
foreach (SearchResult result in mySearcher.FindAll())
{
if (result != null)
{
MessageBox.Show("computer GetDirectoryEntry():" + result.Path+"\n"+"computer path: "+result.Path);
DirectoryEntry entryToRemove = new DirectoryEntry(result.Path,userName,userPwd);
entry.Children.Remove(entryToRemove);
return true;
}
else
{
return false;
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
return false;
}
If you're on .NET 3.5 and up (if you're not - time to upgrade!), you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find the computer in question
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity(ctx, "NAME");
// if found - delete it
if (computer != null)
{
computer.Delete();
}
The new S.DS.AM makes it really easy to play around with users, computers and groups in AD!
Using ADSI which is under System.DirectoryServices use a commit mechanism, here is a working sample :
/* Retreiving RootDSE infos
*/
string ldapBase = "LDAP://WM2008R2ENT:389/";
string sFromWhere = ldapBase + "rootDSE";
DirectoryEntry root = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
string defaultNamingContext = root.Properties["defaultNamingContext"][0].ToString();
/* Retreiving the computer to remove
*/
sFromWhere = ldapBase + defaultNamingContext;
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", ".biènèsph^r^.1966");
DirectorySearcher dsLookForDomain = new DirectorySearcher(deBase);
dsLookForDomain.Filter = "(&(cn=MACHSUPR))"; // MACHSUPR is the computer to delete
dsLookForDomain.SearchScope = SearchScope.Subtree;
dsLookForDomain.PropertiesToLoad.Add("cn");
dsLookForDomain.PropertiesToLoad.Add("distinguishedName");
SearchResultCollection srcComputer = dsLookForDomain.FindAll();
foreach (SearchResult aComputer in srcComputer)
{
/* For each computer
*/
DirectoryEntry computerToDel = aComputer.GetDirectoryEntry();
computerToDel.DeleteTree();
computerToDel.CommitChanges();
}
Use WMI and or System.DirectoryServices namespace (http://msdn.microsoft.com/en-us/library/system.directoryservices.aspx).
It may not be exactly what you are looking for, but this site provides a number of code examples for working with AD in C#, including deleting a security group and removing a user from a group

Faster way to find out if a user exists on a system?

I have an application that checks to see if a user exists (if not create it) every time it starts. This is done as follows:
bool bUserExists = false;
DirectoryEntry dirEntryLocalMachine =
new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
DirectoryEntries dirEntries = dirEntryLocalMachine.Children;
foreach (DirectoryEntry dirEntryUser in dirEntries)
{
bUserExists = dirEntryUser.Name.Equals("UserName",
StringComparison.CurrentCultureIgnoreCase);
if (bUserExists)
break;
}
The problem is on the majority of the systems where it is deployed. This can take 6 - 10 seconds, which is too long ... I need to find a way to reduce this (as much as possible). Is there a better or faster way I can use to verify if a user exists on the system or not?
I know there are other ways to solve this, like have the other applications sleep for 10 seconds, or have this tool send a message when it is ready, etc... But if I can greatly reduce the time it takes to find the user, it would make my life much easier.
.NET 3.5 supports new AD querying classes under the System.DirectoryServices.AccountManagement namespace.
To make use of it, you'll need to add "System.DirectoryServices.AccountManagement" as a reference AND add the using statement.
using System.DirectoryServices.AccountManagement;
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine))
{
UserPrincipal up = UserPrincipal.FindByIdentity(
pc,
IdentityType.SamAccountName,
"UserName");
bool UserExists = (up != null);
}
< .NET 3.5
For versions of .NET prior to 3.5, here is a clean example I found on dotnet-snippets
DirectoryEntry dirEntryLocalMachine =
new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
bool UserExists =
dirEntryLocalMachine.Children.Find(userIdentity, "user") != null;
You want to use the DirectorySearcher.
Something like this:
static bool userexists( string strUserName ) {
string adsPath = string.Format( #"WinNT://{0}", System.Environment.MachineName );
using( DirectoryEntry de = new DirectoryEntry( adsPath ) ) {
try {
return de.Children.Find( strUserName ) != null;
} catch( Exception e ) {
return false;
}
}
}
That should be quicker. Also, you can reduce the properties if all you are doing is checking for existence.
Another way is the following (supports either local or domain user):
bool UserExists(string userName)
{
var user = new NTAccount(userName);
try
{
var sid = (SecurityIdentifier)user.Translate(typeof(SecurityIdentifier));
return true;
}
catch (IdentityNotMappedException)
{
return false;
}
}
User may be either unqualified, or qualified by machine/domain name (DOMAIN\UserName). If you need to specifically detect if the account exists on local machine, qualify it by Environment.MachineName ($"{Environment.MachineName}\\{userName}").
The following in a command prompt returns 1 if 'username' exists.
net user | find "username" /c

Categories

Resources