Active Directory C# help

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
Hello All,

Writting a new C# program to replace a very old VBS file. Pretty much the program will go through our AD and get all the users and their emails and put it in a CSV file. I found some basic code and ran it and I think it worked because I saw a huge list in the console but when it was finished it closed and gave me an error.

ERROR
System.ArgumentOutOfRangeException was unhandled
Message="Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"
Source="mscorlib"
ParamName="index"
StackTrace:
at System.Collections.ArrayList.get_Item(Int32 index)
at System.DirectoryServices.ResultPropertyValueCollection.get_Item(Int32 index)
at ConsoleApplication4.Program.Main(String[] args) in C:\Users\jsalina\Documents\Visual Studio 2008\Projects\ConsoleApplication4\ConsoleApplication4\Program.cs:line 26
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:


Code
Code:
using System;
using System.DirectoryServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            DirectoryEntry entry = new DirectoryEntry("LDAP://OU=PHF Users,DC=phf,DC=inc");

            using (DirectorySearcher ds = new DirectorySearcher(entry))
            {
                ds.PropertiesToLoad.Add("name");
                ds.PropertiesToLoad.Add("userPrincipalName");

                ds.Filter = "(&(objectClass=user))";

                SearchResultCollection results = ds.FindAll();

                foreach (SearchResult result in results)
                {
                    Console.WriteLine("{0} - {1}",
                        result.Properties["name"][0].ToString(),
                        result.Properties["userPrincipalName"][0].ToString());
                }
            }

        }
    }
}

Any thoughts on it?


P.S. That just the basic code, trying to get it to work in the console before I add the CSV file and validations. Pretty new to this type of programming.
 
Solution
Did you try digging into name like you did with cn? Also, double check what permissions are setup as AD may not be sending that info as you may not be allowed to see anything except names.

Sunius

Distinguished
Dec 19, 2010
390
0
19,060
What is happening basically is that result doesn't necessarily have properties "name" and "userPrincipalName". You should check whether it has that property first before accessing the first character of that property.
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
That is a good point Sunius, this was the the others guy who was teaching it names, I want to connect to the Ldp.exe and connect the the AD and get the exact names. Just new to this so want to ask boss to make sure I dont mess anything up in the company.

and majestic pretty new at this, All i know from college is basic c++ and java.
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
That is a good point Sunius, this was the the others guy who was teaching it names, I want to connect to the Ldp.exe and connect the the AD and get the exact names. Just new to this so want to ask boss to make sure I dont mess anything up in the company.

and majestic pretty new at this, All i know from college is basic c++ and java.
 

majestic1805

Honorable
Oct 1, 2012
69
0
10,590
That's fine. Was just pointing out that I didn't have to do any custom LDAP parsing or building my own AD interaction objects. Just keep in mind that you'll only have permissions to do what the .Net machine account has rights to do within AD. I would strongly suggest you set up an account with which to impersonate for AD interaction even if it's a user with only read rights.
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
Here's the current code that is giving me an error. Just want it to work so I can move on and code the other things I need.

Code:
using System;
using System.DirectoryServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            DirectoryEntry entry = new DirectoryEntry("LDAP://OU=PHF Users,DC=phf,DC=inc");

            using (DirectorySearcher ds = new DirectorySearcher(entry))
            {
                ds.PropertiesToLoad.Add("cn");
                ds.PropertiesToLoad.Add("mail");

                ds.Filter = "(&(objectClass=user))";

                SearchResultCollection results = ds.FindAll();

                foreach (SearchResult result in results)
                {
                    Console.WriteLine("{0} - {1}",
                        result.Properties["cn"][0].ToString(),
                        result.Properties["mail"][0].ToString());
                }

            }

            

        }
    }
}
 

majestic1805

Honorable
Oct 1, 2012
69
0
10,590
Your problem is that you're assuming a state that isn't true by trying to go for the first element in the Properties collection when doing as you have done clearly doesn't have a first element. You can dig in and find out this info for yourself and I highly recommend it. Debug the result object to see what the Properties collection looks like and adjust your code accordingly.

If the Properties collection looks nothing like you'd expect then your premise about how that object works is wrong and you'll need to examine why.
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
I am so confused, let me try to walk through the code and see if I understand it correctly.

1)We open PHF Users directory in the AD and set that path to entry.
2)We create a search of that directory using ds.
3)We then say we only want to Users Name and Email and set a filter to only grab Users
4)Then we set a search off all the data and put into results
5)Go through all the results only pulling out the CN and MAIL categories

Are you saying that the results are pulling every single category not just the CN and MAIL? (ie. accountExpires,badPasswordTime,badPwdCount......ect)
 

majestic1805

Honorable
Oct 1, 2012
69
0
10,590
I'm saying you're treating your 5th step in a way that's not meshing with how the results object actually works. You need to hook up a debugger and dig into the Properties collection to see how that object is being populated, if at all, and to adjust your code accordingly. Maybe the key name is different, maybe there is no element 0, etc. These are things that will become evident once you hook up a debugger.
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
ok bare with me, I am pretty new at this debugging. Found the results and PropertiesLoaded and its saying its a string[3], [0]="cn" [1]="mail" and [2] ="ADsPath"(which idk what that is) Could this be the problem?
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
so many collapsable menus, i went to:

result->Properties->PropertyName->non-public members->["cn"]->value->base->non-public members->list->non-public members->_items->
[0] "vault system"
[1] null
[2] null
[3] null

was that totally useless?
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
when i hover over result.Properties["cn"][0].ToString(), the result says null on the little drop down, if it was working correctly I am guessing it would have a name in it right?
 

laserpp

Distinguished
Nov 29, 2008
137
0
18,630
Ok I got rid of that other code and followed another coder on YouTube, his code seems to work, getting all the names, but when i try to add the mail component it errors, gonna work on in a little and ill be back
 

majestic1805

Honorable
Oct 1, 2012
69
0
10,590
Code:
        static void Main(string[] args)
        {
            var dc = new DirectoryContext(DirectoryContextType.Domain);

            var d = Domain.GetDomain(dc);

            var s = d.FindDomainController().GetDirectorySearcher();

            s.Filter = "(&(objectClass=user))";

            var f = s.FindOne();

            Console.WriteLine(f.Path);
            Console.WriteLine(f.Properties["cn"][0]);
            Console.WriteLine(f.Properties["mail"][0]);

            Console.ReadLine();
        }

This works fine for me. Perhaps try getting your searcher object using this method instead?