Hi,
well, I do have some ideas on speeding it up.
- First of all, you could pre-initialize the dictionary to maximum size necessary (Number of Users in Group + Number of Users Database says ought to be in there). This saves you time from resizing.
- Optimize Data Lookup: Use ADSI to only lookup the properties you actually need (User: DN and whatever you need to map to DB Entry; Group: DN and Members). Only search for users once and create a reference dictionary for later use.
- Multithread processing. If you have the processing capacity to use, why not have Powershell run in multiple threads?
- Minimize scaling overhead for object generation.
Here's some C# code that can do 1) for you and be helpful in 4) too:
# The source code
$source = @"
namespace Netzwerker.Utility
{
using System;
using System.Collections.Generic;
/// <summary>
/// Class that contains methods that return specific kinds of objects that are hard to generate directly from Powershell (especially generics)
/// </summary>
public static class New
{
/// <summary>
/// Returns a dictionary with the predefined types of key and value.
/// </summary>
/// <param name="Key">The type of the Dictionary Key.</param>
/// <param name="Value">The type of the Dictionary Value.</param>
/// <param name="Size">The initial Size of the dictionary. Increase for better performance, reduce for less memory consumption.</param>
/// <returns>Returns a dictionary with the predefined types of key and value.</returns>
public static object Dictionary(Type Key, Type Value, int Size)
{
Type genericClass = new Dictionary<string, int>().GetType().GetGenericTypeDefinition();
Type constructedClass = genericClass.MakeGenericType(Key, Value);
return Activator.CreateInstance(constructedClass, Size);
}
/// <summary>
/// Creates an empty, generic list of the appropriate type.
/// </summary>
/// <param name="T">The Type of the class to create</param>
/// <returns>A System.Collections.Generic.List object of the passed type</returns>
public static object List(Type T)
{
Type genericClass = new List<string>().GetType().GetGenericTypeDefinition();
Type constructedClass = genericClass.MakeGenericType(T);
return Activator.CreateInstance(constructedClass);
}
}
public struct UserEntry
{
public string UserDN;
public string GroupDN;
public bool Old;
public bool New;
public UserEntry(string UserDN, string GroupDN, bool Old, bool New)
{
this.UserDN = UserDN;
this.GroupDN = GroupDN;
this.Old = Old;
this.New = New;
}
public bool ToAdd
{
get
{
return ((!Old) && New);
}
}
public bool ToRemove
{
get
{
return ((!New) && Old);
}
}
}
}
"@
# Add C# Code
Add-Type $source
# Create a new, presized Dictionary
$Dictionary = [Netzwerker.Utility.New]::Dictionary("String", "Netzwerker.Utility.UserEntry", 2000)
Basically you fill the info from DB and DL for each user in such an object, and the UserEntry will automatically process the
ToAdd and ToRemove properties.
Regarding 3, you may find
this function useful. Note that you need not necessarily use default limits for concurrent threads. Exceeding the number of cores available is fully valid if there's some waiting period involved (like AD interaction).
As for 2, you can initially search and generate a list of all users and universal groups each and store it in a properly sized dictionary which can later be looked up.
Cheers,
Fred