Created
February 24, 2013 20:47
-
-
Save cabrel/5025563 to your computer and use it in GitHub Desktop.
Querying WMI for local users
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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