In the final part of this series, I will look at dealing with the lastlogontimestamp attribute and easily outputting the data you have gathered to a file.
Part 2 left us with :
As we are dealing with a foreach loop to dynamically output all our properties, we can use a switch statement to deal with the lastlogontimestamp property separately from the rest of the attributes. (NOTE : we could use an if statement but putting a switch in now makes further exceptions much easier to add later on).
so the (partial) script looks like this :
As we have now converted our data to useful information, let's output it using a few of powershells built in mechanisms. When I first started doing this searcher, i was still very much in the VB way of thinking. I created a string with all the variables in it seperated with commas and used add-content to dump to a file throughout the script. My friend and old work colleague Josh, showed me the way forward by using Arrays.
First we need to create an array full of the variables that we are searching for. Here we can use $objectproplist again ( I told you it would come in handy!) :
Very handy!
We can now use our foreach loop and switch to populate the $Store properties :
We now have a single array for each returned value, that unfortunately gets overwritten with every new object. We need a means of storing each array .... how about another array?
If we define a new array a the begining of the script $Collection = @(), then on completion of each result loop, we can output the $store into the collection :
so the whole script looks like this :
Let me know if the scripts haven't translated to Blog 100% as I am still getting used to the editor!
Cheers!
Part 2 left us with :
$domains = [System.DirectoryServices.ActiveDirectory.forest]::getcurrentforest().get_domains()
$ObjectCategory = "user"
$ObjectProplist = "name","samaccountname","whencreated"
$LdapQuery = "(&(objectCategory=$ObjectCategory)(name=ad*))"
foreach ($domain in $domains){
($domain).name
$LDAPdomain = $domain.GetDirectoryEntry()
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($LDAPdomain, $LdapQuery, $ObjectProplist,"subtree")
$Searcher.pagesize = 1000
$Results = $Searcher.FindAll()
foreach ($Object in $Results){
foreach ($prop in $ObjectProplist){
}$Object.Properties.$prop
}
If you have tried to manipulate lastlogontimestamp in VB, you will know it is not a friendly attribute. That is until powershell gets involved. If you output the value of lastlogontimestamp you should get somethin like this : 128979678074825862. Not very helpful really, unless you know how many milliseconds it has been since 1 January 1601. Once again, .net comes in to help us. the method [DateTime]::FromFileTimeUTC() can be used to convert the value into something more readable. The above number for example converts to "21/09/2009 12:50:07 AM". Much better!}
As we are dealing with a foreach loop to dynamically output all our properties, we can use a switch statement to deal with the lastlogontimestamp property separately from the rest of the attributes. (NOTE : we could use an if statement but putting a switch in now makes further exceptions much easier to add later on).
so the (partial) script looks like this :
foreach ($prop in $ObjectProplist){NOTE : I will be posting useful ldap queries soon which will include how to use [DateTime]::ToFileTimeUTC to query on lastlogontimestamp.
switch ($prop){
lastlogontimestamp {[DateTime]::FromFileTimeUTC($Object.Properties.lastlogontimestamp[0])}
Default {$Object.Properties.$prop}
}
}
As we have now converted our data to useful information, let's output it using a few of powershells built in mechanisms. When I first started doing this searcher, i was still very much in the VB way of thinking. I created a string with all the variables in it seperated with commas and used add-content to dump to a file throughout the script. My friend and old work colleague Josh, showed me the way forward by using Arrays.
First we need to create an array full of the variables that we are searching for. Here we can use $objectproplist again ( I told you it would come in handy!) :
$Store = "" | select $ObjectProplistIf you now output $Store you will see that all the $ObjectProplist values are now properties of the array $store :
Very handy!
We can now use our foreach loop and switch to populate the $Store properties :
PS C:\> $Store
name adspath lastlogontimestamp
---- ------- ------------------
We now have a single array for each returned value, that unfortunately gets overwritten with every new object. We need a means of storing each array .... how about another array?
foreach ($prop in $ObjectProplist){
$Store = "" | select $ObjectProplist
switch ($prop){
lastlogontimestamp {$Store.$prop = [DateTime]::FromFileTimeUTC($Object.Properties.lastlogontimestamp[0])}
Default {$Store.$prop = $Object.Properties.$prop}
}
}
If we define a new array a the begining of the script $Collection = @(), then on completion of each result loop, we can output the $store into the collection :
$Collection = @()
$domains = [System.DirectoryServices.ActiveDirectory.forest]::getcurrentforest().get_domains()
$ObjectCategory = "user"
$ObjectProplist = "name","samaccountname","whencreated"
$LdapQuery = "(&(objectCategory=$ObjectCategory)(name=ad*))"
foreach ($domain in $domains){
($domain).name
$LDAPdomain = $domain.GetDirectoryEntry()
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($LDAPdomain, $LdapQuery, $ObjectProplist,"subtree")
$Searcher.pagesize = 1000
$Results = $Searcher.FindAll()
foreach ($Object in $Results){
$Store = "" | select $ObjectProplist
foreach ($prop in $ObjectProplist){
switch ($prop){
lastlogontimestamp {$Store.$prop = [DateTime]::FromFileTimeUTC($Object.Properties.lastlogontimestamp[0])}
Default {$Store.$prop = $Object.Properties.$prop}
}
}
$Collection += $Store
}
nearly done! All we need to do is output to csv :}
$Collection | export-csv "outfile.csv" -NoTypeInformationDue to the nature of powershell, everything returned is an object, you will sometimes get an object definition in your csv file rather than the actual value. By adding this to your default case in your switch statement, a string will be output instead. (most objects have the tostring() method!)
Default {$Store.$prop = $($Object.Properties.$prop).tostring()}
so the whole script looks like this :
$Collection = @()
$domains = [System.DirectoryServices.ActiveDirectory.forest]::getcurrentforest().get_domains()
$ObjectCategory = "user"
$ObjectProplist = "name","samaccountname","whencreated"
$LdapQuery = "(&(objectCategory=$ObjectCategory)(name=ad*))"
foreach ($domain in $domains){
($domain).name
$LDAPdomain = $domain.GetDirectoryEntry()
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($LDAPdomain, $LdapQuery, $ObjectProplist,"subtree")
$Searcher.pagesize = 1000
$Results = $Searcher.FindAll()
$Store = "" | select $ObjectProplistforeach ($Object in $Results){
foreach ($prop in $ObjectProplist){
switch ($prop){
lastlogontimestamp {$Store.$prop = [DateTime]::FromFileTimeUTC($Object.Properties.lastlogontimestamp[0])}
Default {$Store.$prop = $($Object.Properties.$prop).tostring()}
}
}
$Collection += $Store
}
}
$Collection | export-csv "outfile.csv" -NoTypeInformationSo that's about it. You can go further and use the trap statement to catch errors or deal with exceptions in the switch statement, or if you encounter the default 120 second LDAP timeout with a big query (as I have) you can start doing recursive calls with a function which enables you to really start controlling where you search in your OU structure.
Let me know if the scripts haven't translated to Blog 100% as I am still getting used to the editor!
Cheers!
you write:
ReplyDelete================
foreach ($Object in $Results){
foreach ($prop in $ObjectProplist){
$Store = "" | select $ObjectProplist
===============
But $Store shouldn't re-initialize itself within the $prop loop, it should be
foreach ($Object in $Results){
$Store = "" | select $ObjectProplist
foreach ($prop in $ObjectProplist){
...
Hi George
ReplyDeleteThanks for the feedback, script updated!
regards
Adam