Return to Snippet

Revision: 31823
at September 14, 2010 10:05 by cabrel


Initial Code
/// <summary>
/// Returns a list of users on a target machine or machines which could
/// include an additional domain to search in.
/// </summary>
/// <param name="connection"></param>
/// <param name="domain"></param>
/// <param name="hosts"></param>
/// <returns></returns>
public List<string> GetLocalUsers(ConnectionOptions connection, string domain, ConcurrentBag<string> hosts)
{
    // For storing any exceptions
    var ex = new ConcurrentBag<string>();

    // Stores our hosts and any groups found on the local machine
    var hostCollection = new ConcurrentDictionary<string, string>();

    // holds any of our results
    var results = new ConcurrentBag<string>();

    // Since there are multiple user groups on each machine
    // we want to know them all, so we go and find them
    //
    // This will search each machine for the groups it contains
    // and then store it in our host collection.
    foreach (var h in hosts)
    {
        var groups = RunGetGroups(connection, h);
        hostCollection.TryAdd(h, groups);
    }

    // Since we don't know ahead of time how many hosts
    // we could have, we want to try to speed this up as
    // much as possible.
    //
    // Parallel.ForEach allows us to iterate over the collection
    // and push each iteration into a parallel state, meaning we
    // can search a number of machines at once instead of one
    // by one.
    Parallel.ForEach(hostCollection, col =>
    {
        var groups = col.Value.Split(',');
        var tsafeGroups = new ConcurrentBag<string>(groups);

        // The same as above but now since we have multiple groups
        // per machine we want to make sure we don't get stuck on a
        // machine that might have a large number of local groups on it.
        Parallel.ForEach(tsafeGroups, group =>
        {

            // This is the formatted string we'll use when we connect to the remote
            // machine.
            //
            // we are telling the future connector that on the given hostname 
            // we are searching in root\cimv2
            var scopeFormat = String.Format("\\\\{0}\\root\\cimv2", col.Key);

            // This is constructing our clause to only choose user accounts
            // that are part of the local computer domain
            StringBuilder sb = new StringBuilder("GroupComponent=");
            sb.Append('"');
            sb.Append("Win32_Group.Domain=");
            sb.Append("'");
            sb.Append(col.Key);
            sb.Append("'");
            sb.Append(",Name=");
            sb.Append("'");
            sb.Append(group);
            sb.Append("'");
            sb.Append('"');

            // If user passed in a domain, be sure to include it as well
            if (!String.IsNullOrEmpty(domain))
            {
                sb.Append(" or ");
                sb.Append("GroupComponent=");
                sb.Append('"');
                sb.Append("Win32_Group.Domain=");
                sb.Append("'");
                sb.Append(domain.ToUpper());
                sb.Append("'");
                sb.Append(",Name=");
                sb.Append("'");
                sb.Append(group);
                sb.Append("'");
                sb.Append('"');
            }

            // I haven't looked to much into it but WMI seems to throw a lot of exceptions
            // some are important and some seem to be a by product of cross platform or
            // remote querying.
            //
            // We catch them all anyway and store them but we don't do anything with them here
            //
            try
            {
                var scope = new ManagementScope(scopeFormat, connection);

                // Here is where we apply our condition we built above to the query
                var sQuery = new SelectQuery("Win32_GroupUser", sb.ToString());
                var searcher = new ManagementObjectSearcher(scope, sQuery);

                if (searcher != null)
                {

                    // Returns our list of objects which match our above SelectQuery
                    ManagementObjectCollection mObjects = searcher.Get();

                    if (mObjects.Count > 0)
                    {
                        foreach (ManagementObject obj in mObjects)
                        {
                            var path = new ManagementPath(obj["PartComponent"].ToString());

                            if (path.ClassName == "Win32_UserAccount")
                            {

                                // The relativepath string looks like this:
                                // Win32_UserAccount.Domain="DOMAIN",Name="Username"
                                //
                                // The domain could be either the local computer name or
                                // if the user passed in a domain to search for then that
                                // domain as well
                                //
                                // We now have to split it up and pull out the pieces we need
                                //

                                string[] names = path.RelativePath.Split(',');

                                // the domain
                                var memberOf = names[0].Substring(names[0].IndexOf("=") + 1).Replace('"', ' ').Trim();

                                // the username
                                var name = names[1].Substring(names[1].IndexOf("=") + 1).Replace('"', ' ').Trim();

                                // This formats a nice csv string in order of:
                                // hostname, group name, domain name, username
                                var output = String.Format("{0},{1},{2},{3}", col.Key, group, memberOf, name);
                                results.Add(output);
                            }
                        }
                    }
                }
            }
            catch (Exception e1)
            {
                ex.Add(String.Format("{0}: {1}", col.Key, e1.ToString()));
            }
        });
    });

    return results.ToList<string>();
}

Initial URL


Initial Description


Initial Title
Querying WMI for local users

Initial Tags
windows, user

Initial Language
C#