Mike H. - Another Geek In Need...

WebLog

  Home  |   Contact  |   Syndication    |   Login
  58 Posts | 6 Stories | 195 Comments | 294 Trackbacks

News

Archives

Post Categories

Image Galleries

Development

Favorite Blogs

Hosting

User Groups

As you venture into this aspect of development, you will likely use 1 of 2 assemblies to provide you access to Active Directory (AD) or other directory services providers (DSP's). Microsoft's System.DirectoryServices is the most fundamental - providing core LDAP (lightweight directory access protocol) access to AD and its schema/components. The other is Microsoft's Active Directory Services Interface assembly (ADSI) - the ActiveDs.DLL - which is not so documented but provides a hoard of features for directory services development.

Here I am going to introduce some basic fundamentals/concepts in an effort to help you understand how these core components interact with AD from your application. In my travels down this path I have found very little in News Groups, on TechNet, or in the development community in general that address the basic fundamentals: Most of my findings are support snipets that assume you understand a lot of core components/constructs already. Most people however, do not.

As this series continues, we will cover basic AD interfaces that allow your application to interact with AD, users and objects, and update / maintain these objects. We'll gradually venture into more complex topics like security and Access Control Entries (ACE's) and Access Control Lists (ACL's) - which are the underlying interfaces/components that will provide you the ability to deliver directory services applications feature-rich in functionality and utility.

The only assumptions we'll make here are:

  • You are familiar with Visual Studio and basic development practices.
  • You understand what assemblies / references are in the IDE / application projects.
  • You are comfortable with the C# language and .Net in general.

Finally, this is not a definitive guide or approach - it is simply the musings of another Geek in Need - and I hope that conveying some of this knowledge is helpful to other developers.

So let's begin!!!

System.DirectoryServices Namespace

At the core of this namespace is the primary class DirectoryEntry. There is more here than we will cover for now, but I want to start off simple.

DirectoryEntry is the class you'll use to connect to AD, modify properties, rename or move an AD object, enumerate child objects, create child objects, delete child objects, and collect properties on objects in AD.

For this review, we'll use an AD server I have that has a DNS of developer.hamilton.com, and it is important to note the domain name components here. DirectoryEntry uses specific naming identifiers to bind and interact with directory services components. Here are some we'll address initially:

  • OU - organizationalUnitName, or organizational unit. Note however, how I spelled the initial name - using Camel Case and the fully qualified name. Throughout this series it is important to note this convention as AD uses property names in this fashion - and we will get into that in later parts.
  • CN - commonName - or cononical name. Examples are Mike Hamilton. In directory services this object is references as CN=Mike Hamilton. We'll cover this more shortly.
  • DC - domainComponent. Remember the server name above? Fully qualifying a reference to this in a binding to a DirectoryEntry is DC=developer,DC=hamilton,DC=com. I'll cover more on this in a moment too.

Lets say that Mike Hamilton is a user in AD, in the Users OU on the developer.hamilton.com domain. The following is a general reference in how you would qualify this binding in DirectoryEntry:

LDAP://developer.hamilton.com/CN=Mike Hamilton,OU=Users,DC=developer,DC=hamilton,DC=com

When binding with LDAP (or directory services) you'll notice that the connection string (for lack of a better way to describe it) is in a reverse tree order. If you were to browse this entry with Active Directory Users & Computers, you would expand developer.hamilton.com as the root node, then expand Users, and finally you would see all of the user objects in that node. If I were to create my domain user accounts in an Accounts OU, and further quantify the user objects by department (like Accounting, Development, etc). I might have something more like:

developer.hamilton.com as the root, with an OU off the root called Accounts, and within Accounts I would create OU's called Accounting, and another called Development. Now, lets say that Mike Hamilton is in the Development OU. The connection string would look like:

LDAP://developer.hamilton.com/CN=Mike Hamilton,OU=Development,OU=Accounts,DC=developer,DC=hamilton,DC=com

Now, lets look at a simple C# implementation that will bind and create a DirectoryEntry object reference to this account:

using System;

using System.DirectoryServices;

using System.Collections;

namespace DirectoryServicesAtWork

public class DirectoryUtility

{

             private DirectoryUtility()

             {

                   DirectoryEntry userEntry = new DirectoryEntry(”LDAP://developer.hamilton.com/CN=Mike Hamilton,OU=Developers,OU=Accounts,DC=developer,DC=hamilton,DC=com”);

              }

}

 

Yes, this is a very simple example, but the above code creates an object reference called userEntry and this object will now allow you to inspect, change, and save changes to properties of the user account Mike Hamilton.

ACTIVE DIRECTORY PROPERTIES

There are a host of properties associated with objects in AD. And if you are working with a Windows 2003 forest, there are even more that were added to 2003. For now, I will review a few fundamental ones and demonstrate interaction with those properties.

Lets say I want to get the AD user ID, full name, company name, telephone number and email address of the Mike Hamilton object to display on a form? For this example, we will not create a form or the controls for this display. You will notice the textbox control references easily enough. The purpose here is to give you an example of the syntax and how to reference the appropriate properties.

There are many properties in AD, but we are going to work with only a few for now. As we get into other parts we will review more properties and how to interact with those.

Get User ID, Full Name, Company Name, Telephone Number, and Email Address.

using System;

using System.DirectoryServices;

using System.Collections;

using System.Text;

using System.Forms;

using System.ComponentModel;

namespace DirecetoryUtility

public class frmMain : System.Windows.Forms.Form          // This is the form we'll display our data on...

{

     // Here we'll skip the normal references to our controls (text boxes, etc.)

     public frmMain()

     {

          InitializeComponent();

          DirectoryEntry userEntry = new DirectoryEntry(”LDAP://developer.hamilton.com/CN=Mike Hamilton,OU=Developers,OU=Accounts,DC=developer,DC=hamilton,DC=com”);

          if (userEntry!=null)

          {

               txtUserID.Text = userEntry.Properties[”displayName”].Value.ToString();

               txtFullName.Text = userEntry.Properties[”givenName”].Value.ToString()+” “+userEntry.Properties[”sn”].Value.ToString();

               if (userEntry.Properties[”companyName”].Value.ToString()!=null)

                   txtCompanyName.Text = userEntry.Properties[”companyName”].Value.ToString();

               if (userEntry.Properties[”telephoneNumber”].Value.ToString!=null)

                    txtPhoneNumber.Text = userEntry.Properties[”telephoneNumber”].Value.ToString();

               if (userEntry.Properties[”mail”].Value.ToString()!=null)

                    txtMail.Text = userEntry.Properties[”mail”].Value.ToString();

           }     // End if statement

     }     // end frmMain

}     // End class

Again, this is a very simple example, but it touches on some very important aspects - Property Names in the directory.

Active Directory properties can be a single value, or a collection (array) of values for the property. For example, the userAccountControl property is an enumeration of values that maintain the account status (i.e. Disabled, User Must Change Password, Password Never Expires, and more). We will get more into properties in Part 2 of this feed.

SUMMARY

Here we have only scratched the surface of Directory Services. In the next lesson we will review more in depth the properties of AD DirectoryEntry objects, and how we can manipulate these properties for one purpose or another. We will create a sample application that displays users in a given OU, and allows you to select one from the display and view/modify the properties of that user object.

posted on Friday, September 30, 2005 12:45 PM

Feedback

# re: .Net Directory Services Programming - C# - Part 1 10/28/2005 9:39 AM MarkW
With regards to the userEntry.Properties[”mail”] property, can you guarentee that this will be set. When trying to find a users email address from AD I have had to dig down to the 'proxyAddresses' property, and then loop through it (multivalued) to find the default SMTP address. Unfortunately .net doesn't play nicely at this point and I have received a .net runtime error which then crashes my app.

# re: .Net Directory Services Programming - C# - Part 1 10/28/2005 9:58 AM Michael Hamilton
This is a very good point. I was not going to touch on this until later on - but since you brought it up :)

Yes, if you write to .Properties["mail"].Value you can set/get the property.

Yes, the proxyAddresses property is where up to 3 addresses (and possibly more?) can be stored. The format of these within that property is, respectively:

primary@emailaddy.com
secondary@emailaddy.com
third@emailaddy.com

gets stored like:

SMTP:primary@emailaddy.com
smtp:secondary@emailaddy.com
smtp-pager:third@emailaddy.com

Notice the way the items will be saved in this property.

I was going to get into this property in a later part where we store a complete user record.

I hope this helps...

# re: .Net Directory Services Programming - C# - Part 1 11/7/2005 9:04 AM AQ
I have just started learning AD and LDAP related stuff. This is really good starting stuff for me. I thank the author of this topic.

# re: .Net Directory Services Programming - C# - Part 1 11/17/2005 4:50 PM Jared
Why would you not just set the attribute "pwdLastSet" to 0 to force the user to change his/her password? Are there two ways to accomplish this?

# re: .Net Directory Services Programming - C# - Part 1 11/17/2005 5:18 PM MikeH
That's exactly what the example does - set it to zero.

The TechNet docs state you should set it to -1 - which does just the opposite - it clears the flag.

Also, there are ways to do it with the IADsUser object reference, using the enumerated values from that class.

But the easiest way it just like you said... ["pwdLastSet"].Value = 0;

That'll do it...

# re: .Net Directory Services Programming - C# - Part 1 11/17/2005 6:45 PM Jared
Sort of off subject, but is there a method in any of the DirectoryServices classes that allows you to create an Exchange mailbox for a user? I've created a .NET application to manage users in place of the AD Users & Computers snap-in. I haven't found a mechanism to mailbox-enable users, though. I have to go back to U&C to do this after the user account has been created.

Any suggestions are greatly appreciated.

# re: .Net Directory Services Programming - C# - Part 1 12/12/2005 3:58 PM David Jackson
Here is some code to create a mailbox for a user. Its in VBScript, but you can translate it to C# to do something like this:

internal static void CreateMailbox(DirectoryEntry oDE)
{
const String MDBName = "Mailbox Store (VTESTSERVER)";
const String StorageGroup = "First Storage Group";
const String Server = "VTESTSERVER";
const String AdminGroup = "First Administrative Group";
const String Organization = "First Organization";

String Parameter = "LDAP://CN=" + MDBName +
",CN=" + StorageGroup +
",CN=InformationStore" +
",CN=" + Server +
",CN=Servers" +
",CN=" + AdminGroup +
",CN=Administrative Groups" +
",CN=" + Organization +
",CN=Microsoft Exchange,CN=Services" +
",CN=Configuration," + DomainDN;
//Parameter = "CN=Mailbox Store (VTESTSERVER),CN=First Storage Group,CN=InformationStore,CN=VTESTSERVER,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=vtestdomain,DC=local";
oDE.Invoke("CreateMailbox", Parameter); // creates mailbox in the above mailbox store
oDE.Invoke("SetInfo"); }

# re: .Net Directory Services Programming - C# - Part 1 12/13/2005 9:23 AM MikeH
Thank you for the post David. I've done some work with mailboxes in AD for Exchange - and it's not fun :(

# re: .Net Directory Services Programming - C# - Part 1 12/13/2005 5:40 PM Jeroen Linne
How can i change the smtpmail in the proxyAddresses.
My code is written in vbnet and i am using active directory en cdoexm.

I use the interface Irecipient (nativeobject) en read the property proxyaddresses. When i try to change a line the effect is that there is no change but an extra line is added in the proxyadresses.

example:
proxyadresses(inti) = replace(proxyadresses(inti) , oldsmtp , newstmp)




# re: .Net Directory Services Programming - C# - Part 1 12/15/2005 10:39 PM Louis
I've tried to connect AD with my code like this:
DirectoryEntry userEntry = new DirectoryEntry(”LDAP://developer.hamilton.com/CN=Mike Hamilton,OU=Developers,OU=Accounts,DC=developer,DC=hamilton,DC=com”);
and it worked,but I was not able to get the "userEntry.Properties["givenName"].Value",it seemed that can't be connected,so I checked the network and AD.etc,everything was under control,but it didn't work...

# re: .Net Directory Services Programming - C# - Part 1 12/18/2005 1:38 PM MikeH
Louis... The LDAP string you referenced is the one I used developing the samples at home. I would guess that you changed the LDAP path to a valid one?

Sometimes the DirectoryEntry call appears to work - it doesn't generate an error - but actually does fail to connect - and that would be why the properties are not populated. A quick check is the userEntry.Name property - if the LDAP call failed - trying to read this property will return an exception and you can debug further.

# re: .Net Directory Services Programming - C# - Part 1 12/18/2005 4:20 PM MikeH
Jeroen,

Please see my most recent post on updating this property. Hope it is helpful...

# re: .Net Directory Services Programming - C# - Part 1 1/22/2006 3:52 AM Ditro
I have not read other parts of this article, but how can we get a list of 'Organizationl Uint's in an Active Directory (all elements/sub-elements) using c#?
Thanks much for the post -Ditro

# re: .Net Directory Services Programming - C# - Part 1 1/22/2006 5:03 AM MikeH
Ditro, I apologize but my time has really been limited.

The easiest way is to create your binding context (the DirectoryEntry object), then create a DirectorySearcher object instance.

Set the .Filter to ("OU=*") and you'll get all OU's in that AD.

Hope that's helpful.

# re: .Net Directory Services Programming - C# - Part 1 1/22/2006 5:26 AM Ditro
Great! Thanks for the hint.
I'd just about tried all kinds of filters but ("OU=*") and Ended up writing a recursive method to retrieve the OU's under the root. But this is just great & simple. Not sure why these are not documented as part of the API thou.

# re: .Net Directory Services Programming - C# - Part 1 3/24/2006 6:42 AM Johannes Nielsen
Good stuff, needed som basic info on the subject for a school projekt.

# .Net Directory Services Programming - C# 3/31/2006 2:57 PM Frank
I'm trying to find some code that will display " your password will expire in X days" does anyone know where I can find such code, or does anyone know how to do this? Thanks in advance!

# re: .Net Directory Services Programming - C# - Part 1 4/3/2006 8:28 AM MikeH
Frank, I have been out of town - again - and haven't been able to respond.

I have resolved how to check and see if the user must change their password - but not the actual expiration property. Making this more frustrating is that there isn't a passwordExpires property.

There is an accountExpires property however, but I have not had time to play with this to see if it would be helpful.

I will still be out of town a couple more days - but I will see what I can turn up.

# re: .Net Directory Services Programming - C# - Part 1 4/12/2006 5:26 PM Frank
I figured it out. Here is the code I used:

using System.DirectoryServices;
// You must add a reference to the activeds.tlb in the system32 directory. (click, website > add reference)
// Declare that you are using the ActiveDs namespace.
using ActiveDs;

public partial class StratAccountInfo : System.Web.UI.Page
{
private static string LdapADpath = ConfigurationManager.AppSettings["LdapString"];

protected void Page_Load(object sender, EventArgs e)
{
string LogUser = User.Identity.Name.Substring(User.Identity.Name.IndexOf("\\") + 1).ToString();
lblLoggedInAs.Text = LogUser;
DirectoryEntry entry = new DirectoryEntry(LdapADpath);
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + LogUser + ")";
SearchResult LDAPresult = search.FindOne();
entry = LDAPresult.GetDirectoryEntry();

// Pulling the informtion on when the password was last changed and converting it to a LargeInteger.
LargeInteger liAcctPwdChange = entry.Properties["pwdLastSet"].Value as LargeInteger;

// Convert the highorder/loworder parts of the property pulled to a long.
long dateAcctPwdChange = (((long)(liAcctPwdChange.HighPart) << 32) + (long)liAcctPwdChange.LowPart);

// Convert FileTime to DateTime and get what today's date is.
DateTime dtNow = DateTime.Now;
// I added 90 days because I know what my password expiration is set to, if not you need to pull that information and add the number of days it is set for.
DateTime dtAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange).AddDays(90);
string strAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange).ToShortDateString();
string strAcctPwdExpires = DateTime.FromFileTime(dateAcctPwdChange).AddDays(90).ToShortDateString();

// Calculate the difference between the date the pasword was changed, and what day it is now and display the # of days.
TimeSpan time;
time = dtAcctPwdChange - dtNow;
lblPwdChangedDate.Text = strAcctPwdChange;
lblPassExp.Text = strAcctPwdExpires;
lblPwdExpDays.Text= time.Days.ToString() + " day(s)";
}
}


# re: .Net Directory Services Programming - C# - Part 1 6/8/2006 12:37 PM Afroz Khan
Has any one written code to pull out the password Expiry date from the Active Directory; as in the above ex 90 days is taken; i want this days to be picked from the password policy in Active Directory.

# re: .Net Directory Services Programming - C# - Part 1 6/10/2006 11:22 AM MikeH
Afroz, I apologize for not being able to respond sooner.

I do not think there is a simple way to address this. Group Policy is not stored in the Active Directory schema. Now, the last date a password was changed 'is' stored ["pwdLastSet"] - and this is marshalled as a bigInt so it's not straight forward to read this.

Even so, reading it will simply reveal the date the password was last set (date and time actually) and this may not be helpful if you do not know the policy.

In reviewing the object model for ADSI (ActiveDs.dll) and LDAP (DirectoryServices) I cannot see a way to determine this policy from AD.

It's a great question, and if I come across more on this topic, I will blog it. Sorry I couldn't be of more help now.

# re: .Net Directory Services Programming - C# - Part 1 7/31/2006 12:42 PM Damien
Is there a way that i can get my treeview to display only one folder right now it starts with the root and displays all of them and i just need it to display my mailing lists folder

any ideas

# re: .Net Directory Services Programming - C# - Part 1 7/31/2006 12:58 PM MikeH
Hi Damien,

Can you be more specific about what you are trying to do as it relates to Directory Services programming? I apologize but I am not following you.

# re: .Net Directory Services Programming - C# - Part 1 8/2/2006 12:04 PM Damien
Im programming in C# and have a treeview that displays my whole active directory. I'am trying to get the just my mailling lists to display instead of the whole active directory.example of this is :
private void InitTree1()
{
this.treeView1.Nodes.Clear();
this.treeView1.Nodes.Add(new TreeNode(util.RootContainer.Name, 0, 0));
//Tag TreeNode
this.treeView1.Nodes[0].Tag = new
TreeNodeInfo(util.Path, true);
int index = 0;
foreach (DirectoryEntry child in util.SharedEntry.Children)
{
index = this.treeView1.Nodes[0].Nodes.Add(new TreeNode(child.Name, 3, 4));
//Tag TreeNode
this.treeView1.Nodes[0].Nodes[index].Tag =
new TreeNodeInfo(child.Path, false);
}
this.treeView1.ExpandAll();
}

hope this help understand if not let me know i could use any help i can get.

# re: .Net Directory Services Programming - C# - Part 1 8/4/2006 9:15 AM MikeH
Hi Damien,

It looks like you are trying to build a tree view of contents from a directory schema. Is this correct?

I am not sure how mailing lists are being stored in AD if that is what you are doing.

If you are wanting to display a tree view of specified schema items, you can check the objectClass of the item returned by the .Children (child) object above.

If you are creating custom entries in AD that you want to represent a mailing list, and some of these entries could be active and some not active, an option would be to use a property that is not commonly used in the object schema to set a value indicating the object is active or inactive. Then check for this in your while look - grabbing only the specified mailing list objects, and only those where this property indicates the object is active.

HTH's...

# re: .Net Directory Services Programming - C# - Part 1 8/4/2006 12:23 PM Damien
yup, its a multiline text box that displays a treeview. I'm currently working with a foreach statment to search through and find the OU that I want to display.If you have any ideas let me know or of anwhere online to find more detailed info. that would help to.

# re: .Net Directory Services Programming - C# - Part 1 9/12/2006 7:25 AM 123
Greetings,
I want to create a subdomain programmatically.
Means from the page http://www.somesite.com/createdomain.aspx when i create a subdomain "somename" then it should check if the subdomain already exists else it should create a directory called "somename" and should create a subdomain http://somename.somsite.com ans should map the directory "somename" to that subdomain.
any help wud be appreciated.

Thanks.


# re: .Net Directory Services Programming - C# - Part 1 9/12/2006 8:36 AM MikeH
Hello 123,

If you could provide some sample code demonstrating what you are trying to do, and convey specifically where the problem is occuring, it would be helpful.

Thanks...

# re: .Net Directory Services Programming - C# - Part 1 9/13/2006 2:33 AM 123
Hi MikeH,
What i want to do is have an access to DNS server using code (c#). Is there any way that i can update/add entries to DNS server ? also DNS lookup is needed for checking of existance of an entry prior to adding it. I think there should be some way in WMI or ADSI but am not sure.

# re: .Net Directory Services Programming - C# - Part 1 9/13/2006 8:30 AM MikeH
Hello 123,

I have worked via ADSI API to code against the services architecture in Windows - but it is somewhat limited insofar as the options available to you.

Specifically, I am not familiar with an API to query the DNS service for specific results. The ADSI API simply returns particulars 'about' a service - it does not extend communication with that service.

With that said, I would imagine there is an API for the Windows DNS service, yes. When you consider there are 3rd party DNS services out there you can use on Windows in place of the Windows service - I have to believe there are also API's for those services.

Sorry I could not be of more help.

# re: .Net Directory Services Programming - C# - Part 1 9/21/2006 11:42 PM Sal
Mike,

Just a quick Q for a newbie, I imlpemented your code and it worked fine on our AD. However if I search for a user that doesnt exist I get the follwing error:
"An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in system.directoryservices.dll

Additional information: There is no such object on the server"

How do I check if the user exists?

# re: .Net Directory Services Programming - C# - Part 1 9/22/2006 9:33 AM MikeH
Hi Sal,

If you could copy/paste the code you're working with - maybe I could offer some insight.

# re: .Net Directory Services Programming - C# - Part 1 9/25/2006 6:40 PM Rendili
Hi,
I tried the code from Frank and get the following :
System.NullReferenceException: Object reference not set to an instance of an object

long dateAcctExpiration = (((long)(liAcctExpiration.HighPart) << 32) + (long) liAcctExpiration.LowPart);

any ideas?

Thanks for any help

# re: .Net Directory Services Programming - C# - Part 1 9/25/2006 7:12 PM Rendili
found it, i was setting liAcctExpiration as a long

# re: .Net Directory Services Programming - C# - Part 1 12/19/2006 5:25 AM Clive
Just a huge thanks to all who contributed. I have been seraching the net for about a week to find an easy solution for a novice C# developer working with AD. This is the first article I can follow and understand.

Thanks again

# re: .Net Directory Services Programming - C# - Part 1 1/18/2007 3:50 AM MuddyFox
Hello there! Just came across this blog and thought I might post my question here.
I'm trying to retreive a list of users that have been locked out. Doing InvokeGet("IsAccountLocked") on a DirectoryEntry returns false results, claiming some entries are locked when in fact they are not. Any way around this?
Tnx!

# re: .Net Directory Services Programming - C# - Part 1 1/18/2007 6:52 AM MuddyFox

Just a comment:
I have also tried using this filter
(&(objectCategory=Person)(objectClass=User)(lockoutTime>=1))
but I get the exact same results, which are wrong...


# re: .Net Directory Services Programming - C# - Part 1 1/18/2007 7:26 AM MikeH
Hi MuddyFox,

I have used the following code to check the disabled and locked flags:

DirectoryEntry _currentUser = new DirectoryEntry();
IADsUser iADsUser = (IADsUser)_currentUser.NativeObject;

if (iADsUser.AccountDisabled == true) { yoru code if true }

if (iADsUser.IsAccountLocked == true) { your code if true }

Remember, get the native object in the iADs class and you have more flexibility insofar as checking these flags.

HTH's...

# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 5:53 AM MuddyFox
Tnx for the quick reply, I was afraid this thread might be dead. :)

It has been a while since I did any half-serious development in c# so I might be a bit slow getting things running... please bear with me :)

I tried your code and it still doesn't work. I get exact same results as in previous two cases. Actual locked users and quite a number of users that are neither locked nor disabled.

And btw, users that I manually unlock after running my code still end up displayed as locked???


# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 6:10 AM MikeH
Hey MuddyFox,

One of my posts (http://geekswithblogs.net/mhamilton/archive/2005/10/20/57486.aspx) dealt with reading the flag that indicates that a user must change their password when they log on again.

It sounds either like 1) you may have to find a similar resolve for your solution - involving a more complex reading /setting of the flags, or 2) there is something missing that you simply need to include so you can accurately read these flags.

I have done a number of solutions where I have used these same flags - and I did not have any trouble using the iADS native object. It is very possible that you are not actually getting the native object - that an error is occuring before you attempt that binding - and what is getting returned is an error. I have run into this myself - and it is not always obvious because an exception is not actually thrown.

Can you debug-step through your code - checking the values of the objects as you bind them? In particular check the DirectoryEntry object after creating the binding - and ensure there are no properties set to an error reference, and after binding the iADS user native object, step through an ensure there are no error references in the bindings?

I will try to come up with a more simplified working code snippet and post it for you.

Regards...

MikeH...

# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 6:17 AM MikeH
Hey MuddyFox,

One of my posts (http://geekswithblogs.net/mhamilton/archive/2005/10/20/57486.aspx) dealt with reading the flag that indicates that a user must change their password when they log on again.

It sounds either like 1) you may have to find a similar resolve for your solution - involving a more complex reading /setting of the flags, or 2) there is something missing that you simply need to include so you can accurately read these flags.

I have done a number of solutions where I have used these same flags - and I did not have any trouble using the iADS native object. It is very possible that you are not actually getting the native object - that an error is occuring before you attempt that binding - and what is getting returned is an error. I have run into this myself - and it is not always obvious because an exception is not actually thrown.

Can you debug-step through your code - checking the values of the objects as you bind them? In particular check the DirectoryEntry object after creating the binding - and ensure there are no properties set to an error reference, and after binding the iADS user native object, step through an ensure there are no error references in the bindings?

I will try to come up with a more simplified working code snippet and post it for you.

Regards...

MikeH...

# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 6:37 AM MuddyFox
I really appreciate your help on this... I'll try your suggestion, but in the meantime... I ran across this article and it just might explain all the false positives I'm getting....?
Here:
http://www.mail-archive.com/activedir@mail.activedir.org/msg49139.html

# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 6:43 AM MikeH
Hey MuddyFox,

I have read similar posts - and thanks for that link.

However, I have successfully used the enumerations found in the DirectoryServices namespace - but it takes time to sort through these I'll admin.

Using the iADS native object is the cleanest way to work through these properties. Again, I'll try to write up a more simplified example of how to read this value accurately - I'm at work today - but I'll be home in about 12 hours.

# re: .Net Directory Services Programming - C# - Part 1 1/22/2007 7:28 AM MuddyFox
Hello!

I've done some stepping through the code and casting NativeObject to IADsUser results in many attributes throwing exceptions like this one:
"+ LastFailedLogin 'iADsUser.LastFailedLogin' threw an exception of type 'System.Runtime.InteropServices.COMException' System.DateTime {System.Runtime.InteropServices.COMException}
"

On the other hand, some attributes (e.g. Name, PasswordLastChanged, isAccountLocked) return valid values.


# re: .Net Directory Services Programming - C# - Part 1 1/26/2007 3:49 AM MuddyFox

My code is still not working... I'd prefer not to be a pain in the a**, but I'm on a deadline right now so I have to ask again... any further help you can offer?
Tnx!


# re: .Net Directory Services Programming - C# - Part 1 1/26/2007 4:28 AM MikeH
Hi MuddyFox,

I am on a really bad schedule / dead-line today - but I will try to shed a little more light that may provide the right direction - hopefully.

In the DirectoryServices namespace there are a number of enumerated constants - for example, ADS_UF_ACCONTDISABLE, ADS_UF_LOCKOUT, ADS_UF_NORMAL_ACCOUNT - and there are many more - but these pertain to your problem at hand.

Each of these 'flags' has a hex value. The value for ADS_UF_ACCOUNTDISABLE is 0x0202, or 512 in decimal.

Here is a list of possible flags you may / may not want to work with:

public enum ADS_USER_FLAG_ENUM
{
ADS_UF_SCRIPT = 0X0001,
ADS_UF_ACCOUNTDISABLE = 0X0002,
ADS_UF_HOMEDIR_REQUIRED = 0X0008,
ADS_UF_LOCKOUT = 0X0010,
ADS_UF_PASSWD_NOTREQD = 0X0020,
ADS_UF_PASSWD_CANT_CHANGE = 0X0040,
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0X0080,
ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0X0100,
ADS_UF_NORMAL_ACCOUNT = 0X0200,
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0X0800,
ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0X1000,
ADS_UF_SERVER_TRUST_ACCOUNT = 0X2000,
ADS_UF_DONT_EXPIRE_PASSWD = 0X10000,
ADS_UF_MNS_LOGON_ACCOUNT = 0X20000,
ADS_UF_SMARTCARD_REQUIRED = 0X40000,
ADS_UF_TRUSTED_FOR_DELEGATION = 0X80000,
ADS_UF_NOT_DELEGATED = 0X100000,
ADS_UF_USE_DES_KEY_ONLY = 0x200000,
ADS_UF_DONT_REQUIRE_PREAUTH = 0x400000,
ADS_UF_PASSWORD_EXPIRED = 0x800000,
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000
};

Now, I have seen a decent example in a book called Pro .Net Directory Services Programming available through aPress. The example uses a check box on the screen. If the user checks the checkbox indicating the account should be locked, the account is locked during the saveRecord function - otherwise, it is unlocked. For eg:

//Check to see if the user has elected to disable the user
if (chkDisableUser.Checked == true)
{
//Disable the user
objUser.Properties["userAccountControl"].Value = ADS_USER_FLAG_ENUM.ADS_UF_ACCOUNTDISABLE;
}
else
{
//Normal Account
objUser.Properties["userAccountControl"].Value = ADS_USER_FLAG_ENUM.ADS_UF_NORMAL_ACCOUNT;
}

You will have to call the objUser.CommitChanges() after this code.

What there is NOT a lot of out there are routines that allow you to quickly parse the .Properties["userAccountControl"].Value property/value to determine 'what' flags may / may not be set.

I have seen some examples that use <flagName> || <flagName> and other examples <flagName> AND <flagName> when reviewing the property.

I am sorry that I cannot provide working code snippets as I have not worked with this property a lot myself.

I hope this snippet proves helpful.

For now...

# re: .Net Directory Services Programming - C# - Part 1 1/26/2007 5:25 AM MuddyFox

Quick response is very much appreciated, I'll look this over and post the results...


# re: .Net Directory Services Programming - C# - Part 1 1/29/2007 9:16 AM Jock
Just like to thank the author for a nice introduction to retrieving data from the AD.

# re: .Net Directory Services Programming - C# - Part 1 1/31/2007 8:16 AM MuddyFox
First of all, lemme just thank you for your time trying to straighten this out for me.

My tests show that manipulating these flags (ADS_UF_LOCKOUT included) doesn't yield an accurate list of locked users. I get a list similar to one I used to get with code posted above, but although different, it is still not accurate.

However... having hit a brick wall on this matter, I decided to google some more and this is what I finally came across:

http://dunnry.com/blog/DeterminingIfAUserIsLockedOutUsingLDAPInNET.aspx

I just want to add that this code is the only one that actually worked in my case, and it worked like a charm. Mumblings or not, it served its purpose and I can heartily recommend it...

# re: .Net Directory Services Programming - C# - Part 1 3/21/2007 2:31 AM Rex
Hi,

Is it possible to know user password was expired?
I can check and calculate the expiry date before the password is expired but I cannot separate between incorrect username/password and password expired after the user password was expired.
I am writing code on .net framework 1.1
Anybody can help?


# re: .Net Directory Services Programming - C# - Part 1 3/21/2007 7:01 PM MikeH
Hi Rex,

I'm not sure if this is applicable because I have not tested it. I am wondering - if the password has expired - then the user would be challenged to change their password. Correct?

I blogged here about how to see if the UserMustChangePassword is set - http://geekswithblogs.net/mhamilton/archive/2005/10/20/57486.aspx

Please let me know if that helps any.

# re: .Net Directory Services Programming - C# - Part 1 4/12/2007 9:38 PM blueexpanse
hi, buddy,please do me a favor,help me with the below problem:
I've done the code in an IDE which is installed in a computer(in which the system is winxp) searching ad services which running on a AD_server(win2003 server),
and the codes runs properly; now the problem is once I immigrate the codes to another IDE which
is installed on a win2003 server to try to connect the same AD-service-server,the codes just can not establish
a connection,let alone to get AD-search service.
could you please come up with an answer for me ? many thanks!

# re: .Net Directory Services Programming - C# - Part 1 4/15/2007 6:51 AM MikeH
Hi BlueExpanse,

The first thing to check:

Is the Windows XP box a 'member' of the domain you are connecting to in your code? And, is the Windows 2003 Server - NOT a member of that domain? This can create the issue you're seeing.

What I do - and this works most of the time, and it does not matter if the machine is a member of the domain or not:

In your binding, pass the user ID and password of an account that has credentials to bind to that directory. For example,

DirectoryEntry newEntry = new DirectoryEntry();

binds to the directory ROOT of whatever domain you are a member of - or that the code is running within and it can bind. Further, this will bind using the Windows permissions of the logged on user. Instead of this, try

DirectoryEntry newEntry = new DirectoryEntry("LDAP://my.domain.com,DC=my,DC=domain,DC=com","userName","passWord");

This fully qualifies the domain to bind to, and tells AD who to bind as.

HTH's...

# re: .Net Directory Services Programming - C# - Part 1 12/17/2007 5:41 AM Sagar
hi,
can anybody of the big talents here tell me,
how can we change password of user in Active directory?
i am eagerly looking towards this,
As i read posts in this bolg,
here are really great people talking..
Plz help...

Sagar

# re: .Net Directory Services Programming - C# - Part 1 12/25/2007 6:10 PM MikeH
Hi Sagar... I apologize for not being able to respond sooner.

Please check out this snippet on my new blog and see if that helps.

http://msftliveblogs.com/mhamilton/archive/2007/12/25/how-to-change-a-users-password-in-active-directory-with.aspx

HTH's...

# re: .Net Directory Services Programming - C# - Part 1 2/27/2008 6:52 PM gdwinslow
I don’t now if this is and appropriate question for hear or not, but this is my question:

I am working with windows XPE and Windows XP clients. I need to delete a user and recreate that user to make shore there is now sensitive data left from the user deleted. I used System.DirectoryServices to delete the user account this code:

{
DeleteParam data = (DeleteParam)param;
DirectoryEntry dirEntry = null;
DirectoryEntry NewUser = null;

// delete user when existing
dirEntry = new DirectoryEntry("WinNT://" +
Environment.MachineName +
",computer", ".\\" +
mAdminUser, mAdminPassword);
try
{
// this throws when no such user
NewUser = dirEntry.Children.Find(data.Name, "User");
if(NewUser != null)
dirEntry.Children.Remove(NewUser);
}
// Catch not found exception
catch (COMException cex)
{
Debug.WriteLine(cex.Message);
}
dirEntry.Close();
}

This deletes the User from the User control panel, but do’s not delete the home directory. So I delete the home directory recursively. Then I reboot the Machine and look at the user profiles. There is a user profile called Known profile in the list. This wouldn’t be so bad except every time I delete a user get a new Known profile. And it gets better, there appears to be some kind of registry corruption associated with this as the machine will just plow up after a while. I have pad Microsoft developers to see if they now what is causing this and the answer I got back was it looks like a bug and that was it. I have spent a month with kernel debugging registry comparing with the only conclusion I can com up with is there is more to deleting a user. Do’s anyone now of any profile code tools in C#? Has anyone come across this before? Or am I just being stupid?


# re: .Net Directory Services Programming - C# - Part 1 3/3/2008 6:21 AM Monisha Goyal
Hi tried the following code. But getting the exception
'rootEntry.Name' threw an exception of type 'System.Runtime.InteropServices.COMException'

public LDAPClient.AuthenticationResult IsAuthenticated(string userUID, string password)
{
if (password == null || password == "")
return AuthenticationResult.BadPassword;


DirectoryEntry rootEntry = new DirectoryEntry(hostNamePath, userUID, password, AuthenticationTypes.ReadonlyServer | AuthenticationTypes.Sealing);
try
{
HttpContext.Current.Response.Write(rootEntry.Properties["Name"].Value.ToString());
// Bind to the native AdsObject to force authentication.
Object obj = rootEntry.NativeObject;
DirectorySearcher search = new DirectorySearcher(rootEntry);
search.Filter = "(uid=" + userUID + ")";
search.PropertiesToLoad.Add("uid");
SearchResult result = search.FindOne();
rootEntry.Close();
if (null == result)
return AuthenticationResult.BadPassword;
}
catch (Exception)
{
rootEntry.Close();
return AuthenticationResult.BadPassword;
}
rootEntry.Close();
return AuthenticationResult.UserAuthenticated;
}

# re: .Net Directory Services Programming - C# - Part 1 3/18/2008 3:58 AM ganesh
Hi

I have done coding for adding users to LDAP static groups.
The code works fine when I add users to static group which have few number of uers under it. But when try to add users to static group which have large number of users (around 70K under that group) LDAP code throws exception that "directory service is not operational".
Has anyone faced such type of issue or do you know reason behind this. If yes please let me know .
I have used DirectoryServices libraries in .net.

# re: .Net Directory Services Programming - C# - Part 1 4/4/2008 7:51 AM MikeC
What if i want to List this for ALL USER in a certain OU

- Display Name
- Email
- Phone Number

lets say the domain will be calem.com

and te OU 'IT dep.' but there are more folders (OU) inside this first one... :X

The final idea is to shwo a contact list for the company users.

# re: .Net Directory Services Programming - C# - Part 1 4/24/2008 4:15 PM Sab
How to create an OU in AD?


# re: .Net Directory Services Programming - C# - Part 1 5/9/2008 3:52 PM Allan
Michael,
I am having the hardest time trying to update/add to the directReports attribute of a User object. I have tried multiple ways and continue to recieve an error [A constraint violation occurred.]. I've had this error before when the syntax of the DN that I was adding was incorrectly escaped but I'm sure this one is correct. Here is a snip:

public static void SetListAttribute( DirectoryEntry user, string prop, string val, bool append )
{
if ( ( val != String.Empty ) && ( val != null ) ) //you are trying to add something
{
if ( !append && user.Properties.Contains ( prop ) )
{
// clear the current value and add the new one
user.Properties[prop].RemoveAt ( 0 );
user.CommitChanges ();
}
// just append the value to the attribute
user.Properties[prop].AddRange ( new Object[] { "CN=Dandy\\, Jim,OU=OurCompany Administrators,DC=ourcompany,DC=com" } );
// I've also tried the following:::
user.Properties[prop].Add ( "CN=Dandy\\, Jim,OU=OurCompany Administrators,DC=ourcompany,DC=com" );


//user.Properties[prop][0] = val;
user.CommitChanges ();
}
}

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 2 and 5 and type the answer here: