This blog has moved to http://ThePowerShellGuy.com
Greetings /\/\o\/\/
This blogItem is about a script to get all AD users and computers with the choosen properties in a nested HashTable.
This is a script I actualy used in a Production environment, to update a couple of 1000 policy groups a time ago.
there are 2 reasons i post this now,
1) I needed a nested Hashtable example
2) I had not that much AD coverage yet on my blog
as a quick introduction to the .NET 2.0 AD herplerclasses see :
AD Infastructure exploring with MSH , as at home I did not have AD (I started with adam, see
Monad and Adam (and a preview of my to-be AD provider) but that is not mimicing a "real" AD environment enough to test yet.
but as I had an excuse at work (policy group check, and editing), I made this script to do this action.
the first reason, was a problem I had with sorting an arraylist ("
Wmi-Help Part 1 " Getting All the WMI Classes and Descriptions in a HashTable ) of WMI classes and descriptions.
MSH>$WmiClasses sort key
MSH>$WmiClasses.getEnumerator() sort key
And I did remember to have had the same problems here (using the HashTable created by this script.)
But let's start with the script first :
# Get-ADlist
# Get's al AD users and there Groups in an HashTable
# /\/\o\/\/ 2005
Function Set-ADlist
{
$LDAP = "LDAP://localhost:389/dc=mow,dc=adam"
$root = New-object System.DirectoryServices.DirectoryEntry($LDAP)
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searchItem = "CN"
$searchValue = "*"
$searchClass = "User"
$SearchCat = "*"
$PolGroups = "Pol01","Pol02"
$PropList = "CN","ObjectClass","ObjectCategory","distinguishedName","lastLogonTimestamp"
$searcher.Filter = "(&($($searchItem)=$($searchValue))(objectClass=$($searchClass))(objectCategory=$($SearchCat)))"
echo "Searching on .. $($searcher.Filter)"
$searcher.PageSize = 900
$PropList | foreach {$searcher.PropertiesToLoad.Add($_)}
$searcher.PropertiesToLoad
$Global:ADList = @{}
$ADItems = $searcher.findAll()
$ADItems | foreach {
$Prop = $_.properties
$Line = @{}
$info = @{}
$info.add("CN",$prop.cn)
$info.add("DN",$prop.distinguishedname)
$info.add("Ldap",$prop.adspath)
$info.add("Class",$($prop.objectclass | select -last 1))
$info.add("Logon",$prop.lastlogontimestamp)
$Groups = @{}
$MO = $_.GetDirectoryEntry().MemberOf
$MO | foreach {
$Member = $_
$found = $false
$PolGroups | foreach {
if ($Member -imatch $_)
{
$found = "True"
}
}
$Groups.Add($Member.tostring(),$found)
}
$Line.add("Prop",$Info)
$Line.add("Groups", $groups)
$global:ADList.add($($prop.cn),$Line)
}
}
First you have to change the $LDAP variable to your wanted AD root. (watch that I use Adam here, you don't need to specify a port or even a server, just the AD path, if you want the whole domain, you can even leave out the path, ad the DirectoryEntry will default to the RootDSE then)
The Search path I made a bit flexible by using some Helper variables :
(Makes it more easy to use it in another script, or to change this one)
the Item to search on :
$searchItem = "CN"
The Search Value :
$searchValue = "*"
The Search Class : (but User and Computer are of the "User" Class)
$searchClass = "User"
the Search Category : (here it's * as we want Both Computers as Users)
$SearchCat = "*"
This is a "special"-one I needed to "filter" the policy-groups, it will "Tag" the group as a policygroup, it is used to set a boolean to $true if the group contains one of the strings given.
$PolGroups = "Pol2","Pol1",
the $proplist Array give the properties to load.
$PropList = "CN","ObjectClass","ObjectCategory","distinguishedName","lastLogonTimestamp"
these are than used to "build" the AD searcher, so that we do not have to change that part.
the rest of the script, will use the info collected to generate an array of the found computers and users,
interisting to note may be that I use the Result to create a new directoryEntry in the loop trough the results, as to get the groups I need the DirectoryEntry (as it's not in the resultset), you can get a direct connection to the item by using the GetDirectory Method on a Search Result :
$MO = $_.GetDirectoryEntry().MemberOf
and there is an "extra" loop to check of the Group is in the PolicyGroup Collection.
After you run this script / function You can do things like this :
$ADlist.Computername.prop
$ADlist.Username.groups
etc.
but the Problem came with the more complex usages of this "AD Snapshot" hashTable.
as I could do anything I wanted on 1 line, the querying of the HashTable needed a call to the GetEnumerator that made it a bit complex to read.
I will Post a "brainstorm" session with examples below the Signature,
Watch out !!, as this examples of usage of the hashTable are from the Old session, I found them in Notepad-scrible from the actual action, and I updated them to remove the actual group names,I used part of them to update also, as my Adam is not filled enough to to make new examples at home, I used the saved commands I tryed then, I don't have the output of the command from that time.
but if you can fill the array in you own environment, you can see how powerfull this can be, especialy the walking to the User(in the Snapshot), and the fast querying and testing on the local Hashtable that is very fast, then in the end it's easy to make an action on the resultset (see also examples below) only the usage of .getEnumerator() is making the queries messy I think.
As now I had the same problem with sorting the WMI array, (at that time I was not sure if I did use the ArrayTable right), I did think as this as an good example, where the enumerating "problem" of the hashTable is handycapying a otherwise very powerfull tool.
and, i'm not sure if I did it at the time, but I will throw it up in the NG also, with the following example :
MSH>$ht = @{}
MSH>$ht.add(2,"Two")
MSH>$ht.add(1,"One")
MSH>$ht
Key Value
--- -----
2 Two
1 One
MSH>$ht | sort key
Key Value
--- -----
2 Two
1 One
MSH>$ht.getEnumerator() | sort key
Key Value
--- -----
1 One
2 Two
also this does not work :
$ht | foreach {$_} | sort key
if someone knows a better way, please feel free to leave a comment ;-)
gr /\/\o\/\/
#Computers :
($userlist.getEnumerator()) | foreach {$o = $_ ;if ($o.value.prop.class -eq "computer"){$o.key} }
#Users :
($ADlist.getEnumerator()) | foreach {$o = $_ ;if ($o.value.prop.class -eq "user"){$o.key} }
#Computers in Group:
$group = "CN=Pol1-default,OU=Groups,DC=mow,dc=adam"
($ADlist.getEnumerator()) | foreach {if ($_.value.prop.class -eq "computer"){"$($_.value.prop.cn) $($_.value.groups.ContainsKey($group))"} }
Add comps to group.
$de = New-object System.DirectoryServices.DirectoryEntry
$group = "CN=Pol1-default,OU=Groups,DC=mow,dc=adam"
$de.set_path("LDAP://$group")
($ADlist.getEnumerator()) | foreach {if ($_.value.prop.class -eq "computer"){"$($_.value.prop.cn)";if (!($_.value.groups.ContainsKey($group))){$de.member.add($($_.value.prop.dn));$de.commitchanges()} } }
# All Policy Groups
($ADlist.getEnumerator()) | foreach {$o = $_.key;$($_.value).groups.getenumerator() | where {($_.value) -eq "True"} } |Foreach {$o}
# members of One PolicyGroup
($ADlist.getEnumerator()) | foreach {$o = $_.key;$($_.value).groups.getenumerator() | where {($_.Key) -eq "CN=Pol1-default,OU=Groups,DC=mow,dc=adam"} |Foreach {$o}
$Some Other Tests :
$ADlist.mow.getEnumerator() | where {($_.Value) -eq "True"}
$ADlist.mow.getEnumerator() | where {($_.Value) -eq "True"} | forEach {($_.Key).split(",")[0]}}
$ADlist.getEnumerator() | forEach {$_.key;$_.Value.getEnumerator() | where {($_.Value) -eq "True"}}
$ADlist.getEnumerator() | forEach {$_.key;$_.Value.getEnumerator() | where {($_.Value) -eq "True"} | forEach {($_.Key).split(",")[0]}}
$ADlist.getEnumerator() | forEach {$_.Value.getEnumerator() |where {($_.Key) -eq "CN=Pol1-default,OU=Groups,DC=mow,dc=adam"} }}