Skip to content

Instantly share code, notes, and snippets.

@cabrel
Created February 24, 2013 20:47
Show Gist options
  • Save cabrel/5025563 to your computer and use it in GitHub Desktop.
Save cabrel/5025563 to your computer and use it in GitHub Desktop.
Querying WMI for local users
/// <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>();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment