VBScript/Powershell script to get properties of AD users?

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
Hey all,

I'm trying to modify a vbscript slightly, and quite obviously I have no idea what im doing in vbscript. I'm STARTING to learn powershell, but only have the very basics down, so I could use a little bit of help.

Heres what I have: A vbscript that outputs the names of all of the users in our AD and (after a semicolon delimiter), the last logon time to the network. If theyve never logged on, it shows 1601 as the year. The script is (right now at least) designed to be run from the domain controller.

I'd like to be able to modify this script so that it also posts the date that the user account was created as well as the group memberships that they are a part of (and if I can just pull the name instead of the full distinguished name of the group, thatd be even better).

Heres what I have:

Code:
Option Explicit

Dim objRootDSE, strConfig, adoConnection, adoCommand, strQuery
Dim adoRecordset, objDC
Dim strDNSDomain, objShell, lngBiasKey, lngBias, k, arrstrDCs()
Dim strDN, dtmDate, objDate, objList, strUser
Dim strBase, strFilter, strAttributes, lngHigh, lngLow

' Use a dictionary object to track latest lastLogon for each user.
Set objList = CreateObject("Scripting.Dictionary")
objList.CompareMode = vbTextCompare

' Obtain local Time Zone bias from machine registry.
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Control\" _
    & "TimeZoneInformation\ActiveTimeBias")
If (UCase(TypeName(lngBiasKey)) = "LONG") Then
    lngBias = lngBiasKey
ElseIf (UCase(TypeName(lngBiasKey)) = "VARIANT()") Then
    lngBias = 0
    For k = 0 To UBound(lngBiasKey)
        lngBias = lngBias + (lngBiasKey(k) * 256^k)
    Next
End If

' Determine configuration context and DNS domain from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strConfig = objRootDSE.Get("configurationNamingContext")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory for ObjectClass nTDSDSA.
' This will identify all Domain Controllers.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection

strBase = "<LDAP://" & strConfig & ">"
strFilter = "(objectClass=nTDSDSA)"
strAttributes = "AdsPath"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 100
adoCommand.Properties("Timeout") = 60
adoCommand.Properties("Cache Results") = False

Set adoRecordset = adoCommand.Execute

' Enumerate parent objects of class nTDSDSA. Save Domain Controller
' AdsPaths in dynamic array arrstrDCs.
k = 0
Do Until adoRecordset.EOF
    Set objDC = _
        GetObject(GetObject(adoRecordset.Fields("AdsPath").Value).Parent)
    ReDim Preserve arrstrDCs(k)
    arrstrDCs(k) = objDC.DNSHostName
    k = k + 1
    adoRecordset.MoveNext
Loop
adoRecordset.Close

' Retrieve lastLogon attribute for each user on each Domain Controller.
For k = 0 To Ubound(arrstrDCs)
    strBase = "<LDAP://" & arrstrDCs(k) & "/" & strDNSDomain & ">"
    strFilter = "(&(objectCategory=person)(objectClass=user))"
    strAttributes = "distinguishedName,lastLogon"
    strQuery = strBase & ";" & strFilter & ";" & strAttributes _
        & ";subtree"
    adoCommand.CommandText = strQuery
    On Error Resume Next
    Set adoRecordset = adoCommand.Execute
    If (Err.Number <> 0) Then
        On Error GoTo 0
        Wscript.Echo "Domain Controller not available: " & arrstrDCs(k)
    Else
        On Error GoTo 0
        Do Until adoRecordset.EOF
            strDN = adoRecordset.Fields("distinguishedName").Value
            On Error Resume Next
            Set objDate = adoRecordset.Fields("lastLogon").Value
            If (Err.Number <> 0) Then
                On Error GoTo 0
                dtmDate = #1/1/1601#
            Else
                On Error GoTo 0
                lngHigh = objDate.HighPart
                lngLow = objDate.LowPart
                If (lngLow < 0) Then
                    lngHigh = lngHigh + 1
                End If
                If (lngHigh = 0) And (lngLow = 0 ) Then
                    dtmDate = #1/1/1601#
                Else
                    dtmDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
                        + lngLow)/600000000 - lngBias)/1440
                End If
            End If
            If (objList.Exists(strDN) = True) Then
                If (dtmDate > objList(strDN)) Then
                    objList.Item(strDN) = dtmDate
                End If
            Else
                objList.Add strDN, dtmDate
            End If
            adoRecordset.MoveNext
        Loop
        adoRecordset.Close
    End If
Next

' Output latest lastLogon date for each user.
For Each strUser In objList.Keys
    Wscript.Echo strUser & " ; " & objList.Item(strUser)
Next

' Clean up.
adoConnection.Close
Set objRootDSE = Nothing
Set adoConnection = Nothing
Set adoCommand = Nothing
Set adoRecordset = Nothing
Set objDC = Nothing
Set objDate = Nothing
Set objList = Nothing
Set objShell = Nothing





I've been googling for a while trying to work on this and I've figured out how to get the long LDAP name of the groups, but creation date is eluding me and so is a way to simplify the group names...

Anyone mind giving me a hand with some examples at least so I can start my quest to learn VBscript?
 
Last edited:
 

VinylxScratches

Golden Member
Feb 2, 2009
1,666
0
0
I think I could do this easily with a dsquery with maybe 2 lines of code. Do you have to do it in VBscript?
 

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
Definitely don't have to. I have full access to the DC's, with basically no restrictions (within reason) to the means of doing it.


Basically for auditing purposes for our licensing. Just need a fast and easy way to do the report.
 

VinylxScratches

Golden Member
Feb 2, 2009
1,666
0
0
Well, I can def do something tomorrow when I'm in the office. You are basically wanting to know if a user has logged in, or never has correct? Also like to know when the account has been created? DSquery can do it, but the issue is, it won't come out delimited. You'd have to manipulate it in excel to sort it out fast.
 

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
True. The other thing would be that I need to find out what groups its a member of.

I should be able to play with it in excel to make it work.

Thanks vinyl!
 

KB

Diamond Member
Nov 8, 1999
5,406
389
126
It didn't take long to find this script for enumerating group membership:

http://www.computerperformance.co.uk/vbscript/vbscript_group_user_memberof.htm

the key part is:
strLDAP is the LDAP path contained in adoRecordset.Fields("AdsPath").Value

Set objUser = GetObject(strLDAP)

' Heart of the script, extract a list of Groups from memberOf
objmemberOf = objUser.GetEx("memberOf")
For Each objGroup in objmemberOf
strList = strList & objGroup & vbcr
Next
 

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
Yep, i saw that, and like i said in my OP, i have that and it semi sorta works (wasn't sure if i could get it to return just the actual name of the group instead of the full DN?). My bigger question was the creation date. I'm digging around in dsget and dsquery right now (I had totally forgotten about those!), but I don't see a parameter of creation date, kinda curious to see how vinyl is going about this.
 

VinylxScratches

Golden Member
Feb 2, 2009
1,666
0
0
Code:
dsquery * domainroot -limit 0 -filter "(&(objectCategory=person)(objectClass=user))" -attr createTimeStamp displayName memberof lastlogon > C:\output.txt

The problem is the output is not pretty.... might be better to do it per OU. Also the lastlogon attribute is using some weird method for time... so this query is probably useless :-/
 

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
Actually that helps...but I try it and it doesn't know the -attr parameter. I didn't see it anywhere either which is why I haven't tried it. This is an 03 box, is that something that was added in 08? Tried googling it and didn't come up with too much besides something with powershell?
 

iWantHats

Member
Nov 3, 2010
48
0
0
Code:
dsquery * domainroot -limit 0 -filter "(&(objectCategory=person)(objectClass=user))" -attr [B]createTimeStamp[/B] displayName memberof lastlogon > C:\output.txt

The problem is the output is not pretty.... might be better to do it per OU. Also the lastlogon attribute is using some weird method for time... so this query is probably useless :-/

Actually that helps...but I try it and it doesn't know the -attr parameter. I didn't see it anywhere either which is why I haven't tried it. This is an 03 box, is that something that was added in 08? Tried googling it and didn't come up with too much besides something with powershell?

Try it without the createTimeStamp attribute, I haven't heard of that as a DSQuery attribute before

EDIT: From google/codeidol:

When an object is created or modified in Active Directory, the createTimestamp and modifyTimestamp attributes get set with the current time. The createTimestamp attribute is replicated between domain controllers, so assuming the latest modification of the object in question has replicated to all domain controllers, they will all contain the timestamp when the object was created. (modifyTimestamp is not replicated.)
 
Last edited:

VinylxScratches

Golden Member
Feb 2, 2009
1,666
0
0
Actually that helps...but I try it and it doesn't know the -attr parameter. I didn't see it anywhere either which is why I haven't tried it. This is an 03 box, is that something that was added in 08? Tried googling it and didn't come up with too much besides something with powershell?

I ran the query from my XP box using the administrative tools. Unforunatley, I am unsure what else is needed to get it going. Does DSquery /? work from the command line?
 

Chapbass

Diamond Member
May 31, 2004
3,146
95
91
I tried it again and it workedd, not sure what i did wrong the first time.

To fix the formatting I actually used the same idea you were doing, but I used the freeware app adfind that a lot of people were recommending. Put everything on its own line, which worked out alright.


Unfortunately now my boss has changed some of his requirements, and looks like powershell is going to be the best way to do it.

The upside is im working on learning powershell, so I'll see what i can come up with over the next few days.
 

kamikazekyle

Senior member
Feb 23, 2007
538
0
0
Ok, so this reply is 6 days late, but for those that might be interested (including OP)...

It's pretty easy with PowerShell. Firstly, get the ActiveRoles Management Shell/cmdlets for Active Directory. They're free, and they will make your PowerShell/AD integration MUCH easier.

Secondly, here's a snippet of code that queries the specified OU (recursively), and outputs just the last logon time (no username or anything). Google "ad properties" to get a list of all valid properties of an AD object. Again, this is just a basic overview, but the comments explain what's what. The AR Management shell also integrates into PowerGUI very well.

The only thing below that NEEDS the AD tools is the Get-QADUser cmdlet. You can do without the tools, but it's much more roundabout and not nearly as friendly.

Code:
$s_searchroot = "put.in.your.dc/and ou/path/here"
$i_limit = 100 #Limits the number of returns.  Use 0 for unlimited.

foreach ($s_int_User in (Get-QADUser -SearchRoot $s_searchroot -FirstName * -LastName * -Enabled -SizeLimit $i_limit | Sort DN))  #Sorting optional.  $s_int_User will be your object to manipulate
{
	Write-Host $s_int_User.lastLogon #Write-host to output to command line, calls the lastLogon property from the $s_int_User object
}
 

iWantHats

Member
Nov 3, 2010
48
0
0
Well since this got bumped up, if you need any queries written in powershell I can have them done easily, if I don't already have them done