Archive

Archive for the ‘ADSI’ Category

VBScript: Query multiple servers on any domain from AD or a text file.

March 16, 2011 Leave a comment

I had a requirement to query AD for all servers then gather their network config.  I then found that there were servers in AD that had been shut down but not cleaned up, so the script failed on the first unreachable server, so I had to output a list from AD to a text file then re-work the script to read from that text file.  In this article I’ll show both methods, the worker code and how I output the list of servers.

Enumerate all servers in the domain and query their NIC settings:

Option Explicit

Dim objRootDSE, strDNSDomain, adoConnection, adoCommand, strQuery
Dim adoRecordset, strComputerName, strBase, strFilter, strAttributes

' Determine DNS domain name from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory for all computers.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection

' Search entire domain.
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on computer objects with server operating system.
strFilter = "(&(objectCategory=computer)(operatingSystem=*server*))"

' Comma delimited list of attribute values to retrieve.
strAttributes = "cn"

' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

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

Set adoRecordset = adoCommand.Execute

' Enumerate computer objects with server operating systems.
Do Until adoRecordset.EOF

    ' Worker Code: Enumerate computer objects with server operating systems
    strComputerName = adoRecordset.Fields("cn").Value
    Wscript.Echo "================================="
    Wscript.Echo "Computer Name: " & strComputerName
    Wscript.Echo "================================="
    ' Bind to CIMV2 on each Server
    Set objWMIService = GetObject("winmgmts:" _
        & "{impersonationLevel=impersonate}!\\" & strComputerName & "\root\cimv2")
    ' Gather Network Adapter Configuration
    Set colAdapters = objWMIService.ExecQuery _
        ("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")
    n = 1
    WScript.Echo
    For Each objAdapter in colAdapters
        WScript.Echo
        ' Display NIC Name
        WScript.Echo "Network Adapter " & n
        WScript.Echo "================="
        WScript.Echo "  Description: " & objAdapter.Description
        ' Display MAC address
        WScript.Echo "  Physical (MAC) address: " & objAdapter.MACAddress
        ' Display IP address
        If Not IsNull(objAdapter.IPAddress) Then
            For i = 0 To UBound(objAdapter.IPAddress)
                WScript.Echo "  IP address:             " & objAdapter.IPAddress(i)
            Next
        End If
        ' Display Subnet Mask
        If Not IsNull(objAdapter.IPSubnet) Then
            For i = 0 To UBound(objAdapter.IPSubnet)
                WScript.Echo "  Subnet:                 " & objAdapter.IPSubnet(i)
            Next
        End If
        ' Display Default Gateway
        If Not IsNull(objAdapter.DefaultIPGateway) Then
            For i = 0 To UBound(objAdapter.DefaultIPGateway)
                WScript.Echo "  Default gateway:        " & _
            objAdapter.DefaultIPGateway(i)
        Next
        End If
        ' Display DNS Server Search Order
        WScript.Echo
        WScript.Echo "  DNS"
        WScript.Echo "  ---"
        WScript.Echo "    DNS servers in search order:"
        If Not IsNull(objAdapter.DNSServerSearchOrder) Then
            For i = 0 To UBound(objAdapter.DNSServerSearchOrder)
                WScript.Echo "      " & objAdapter.DNSServerSearchOrder(i)
            Next
        End If
        WScript.Echo "    DNS domain: " & objAdapter.DNSDomain
        ' Display Suffix Search List
        If Not IsNull(objAdapter.DNSDomainSuffixSearchOrder) Then
            For i = 0 To UBound(objAdapter.DNSDomainSuffixSearchOrder)
                WScript.Echo "    DNS suffix search list: " & _
                objAdapter.DNSDomainSuffixSearchOrder(i)
            Next
        End If
        n = n + 1
    Next
adoRecordset.MoveNext
Loop
' Clean up.
adoRecordset.Close
adoConnection.Close
Function WMIDateStringToDate(utcDate)
WMIDateStringToDate = CDate(Mid(utcDate, 5, 2)  & "/" & _
Mid(utcDate, 7, 2)  & "/" & _
Left(utcDate, 4)    & " " & _
Mid (utcDate, 9, 2) & ":" & _
Mid(utcDate, 11, 2) & ":" & _
Mid(utcDate, 13, 2))
End Function

Query AD for a list of servers by saving the following script as QueryADServers.vbs:

Option Explicit

Dim objRootDSE, strDNSDomain, adoConnection, adoCommand, strQuery
Dim adoRecordset, strComputerName, strBase, strFilter, strAttributes

' Determine DNS domain name from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory for all computers.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection

' Search entire domain.
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on computer objects with server operating system.
strFilter = "(&(objectCategory=computer)(operatingSystem=*server*))"

' Comma delimited list of attribute values to retrieve.
strAttributes = "cn"

' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

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

Set adoRecordset = adoCommand.Execute

' Enumerate computer objects with server operating systems.

Do Until adoRecordset.EOF

    ' Worker Code: Enumerate computer objects with server operating systems & display their names
    strComputerName = adoRecordset.Fields("cn").Value
        Wscript.Echo "Computer Name: " & strComputerName
adoRecordset.MoveNext
Loop
' Clean up.
adoRecordset.Close
adoConnection.Close
Function WMIDateStringToDate(utcDate)
WMIDateStringToDate = CDate(Mid(utcDate, 5, 2)  & "/" & _
  Mid(utcDate, 7, 2)  & "/" & _
    Left(utcDate, 4)    & " " & _
      Mid (utcDate, 9, 2) & ":" & _
        Mid(utcDate, 11, 2) & ":" & _
          Mid(utcDate, 13, 2))
End Function

Output a list of servers to a text file by running the script thusly:

C:\>cscript //nologo QueryADServers.vbs > ServerList.txt

NB: Once I output the list of servers I fed the list into a batch file that ran an nslookup against them and removed the ones that didn’t have an entry in DNS.  The resulting ServerList.txt file is the one I used for the following script.

Query the ServerList.txt text file by running the following script from the same directory that the ServerList.txt file is in as Administrator:

Option Explicit

Dim objFSO, objFile, strLine, objWMIService, colAdapters, n, objAdapter, i

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("ServerList.txt", 1)

Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine

    ' Worker Code: Enumerate computer objects with server operating systems
    strComputerName = adoRecordset.Fields("cn").Value
    Wscript.Echo "================================="
    Wscript.Echo "Computer Name: " & strLine
    Wscript.Echo "================================="
    ' Bind to CIMV2 on each Server
    Set objWMIService = GetObject("winmgmts:" _
        & "{impersonationLevel=impersonate}!\\" & strComputerName & "\root\cimv2")
    ' Gather Network Adapter Configuration
    Set colAdapters = objWMIService.ExecQuery _
        ("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")
    n = 1
    WScript.Echo
    For Each objAdapter in colAdapters
        WScript.Echo
        ' Display NIC Name
        WScript.Echo "Network Adapter " & n
        WScript.Echo "================="
        WScript.Echo "  Description: " & objAdapter.Description
        ' Display MAC address
        WScript.Echo "  Physical (MAC) address: " & objAdapter.MACAddress
        ' Display IP address
        If Not IsNull(objAdapter.IPAddress) Then
            For i = 0 To UBound(objAdapter.IPAddress)
                WScript.Echo "  IP address:             " & objAdapter.IPAddress(i)
            Next
        End If
        ' Display Subnet Mask
        If Not IsNull(objAdapter.IPSubnet) Then
            For i = 0 To UBound(objAdapter.IPSubnet)
                WScript.Echo "  Subnet:                 " & objAdapter.IPSubnet(i)
            Next
        End If
        ' Display Default Gateway
        If Not IsNull(objAdapter.DefaultIPGateway) Then
            For i = 0 To UBound(objAdapter.DefaultIPGateway)
                WScript.Echo "  Default gateway:        " & _
            objAdapter.DefaultIPGateway(i)
        Next
        End If
        ' Display DNS Server Search Order
        WScript.Echo
        WScript.Echo "  DNS"
        WScript.Echo "  ---"
        WScript.Echo "    DNS servers in search order:"
        If Not IsNull(objAdapter.DNSServerSearchOrder) Then
            For i = 0 To UBound(objAdapter.DNSServerSearchOrder)
                WScript.Echo "      " & objAdapter.DNSServerSearchOrder(i)
            Next
        End If
        WScript.Echo "    DNS domain: " & objAdapter.DNSDomain
        ' Display Suffix Search List
        If Not IsNull(objAdapter.DNSDomainSuffixSearchOrder) Then
            For i = 0 To UBound(objAdapter.DNSDomainSuffixSearchOrder)
                WScript.Echo "    DNS suffix search list: " & _
                objAdapter.DNSDomainSuffixSearchOrder(i)
            Next
        End If
        n = n + 1
    Next
adoRecordset.MoveNext
Loop
' Clean up.
adoRecordset.Close
adoConnection.Close
Function WMIDateStringToDate(utcDate)
WMIDateStringToDate = CDate(Mid(utcDate, 5, 2)  & "/" & _
Mid(utcDate, 7, 2)  & "/" & _
Left(utcDate, 4)    & " " & _
Mid (utcDate, 9, 2) & ":" & _
Mid(utcDate, 11, 2) & ":" & _
Mid(utcDate, 13, 2))
End Function

If you save this script as QueryServersFromTextFile.vbs you can run it as follows to output the servers NIC configs to a text file:

C:\>cscript //nologo QueryServersFromTextFile.vbs > ServerNICs.txt

Sample output:

=================================
Computer Name: DC1.yourdomain.com
=================================

Network Adapter 1
=================
Description: HP Network Team #1
Physical (MAC) address: 00:11:22:33:44:55
IP address:             192.168.77.100
Subnet:                 255.255.255.0
Default gateway:        192.168.77.1

DNS
---
DNS servers in search order:
    192.168.77.10
    192.168.77.11
DNS domain: yourdomain.com
DNS suffix search list: theirdomain.com
DNS suffix search list: our-domain.com

Once again, thanks to Richard Mueller and the writers of WMI Script-O-Matic & EZ AD Script-O-Matic for enabling me to make my job so much easier.

VBScript for Active Directory SID to Name Resolution

January 21, 2011 Leave a comment

I had a requirement to query a SID from another domain after SID to name resolution failed:

Set objDomain = GetObject("LDAP://dc=corporate,dc=contoso,dc=com")
strComputer = "DC1"
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objSWbemServices = objSWbemLocator.ConnectServer _
(strComputer, "root\cimv2", "CORPORATE\%USERNAME%", "%PASSWORD%")
objSWbemServices.Security_.ImpersonationLevel = 3

Set objAccount = objSWbemServices.Get _
("Win32_SID.SID='S-1-2-34-567891234-5678912345-678912345-67890'")
Wscript.Echo objAccount.AccountName

Set objDomain = Nothing
Set objSWbemLocator = Nothing
Set objSWbemServices = Nothing
Set objAccount = Nothing

If you want to query the user name for it’s associated SID, just replace:

("Win32_SID.SID='S-1-2-34-567891234-5678912345-678912345-67890'")
Wscript.Echo objAccount.AccountName

with:

("Win32_UserAccount.Name='%USERNAME%',Domain='CORPORATE'")
Wscript.Echo objAccount.SID

VBScript to Bulk Update UPNs in Active Directory

January 20, 2011 Leave a comment

I had a requirement to harvest all the users in a domain into an Excel spreadsheet then update the ones with the wrong UPN after some faceless entity added the wrong UPN to a number of user accounts:

sDomain="testlab"

Set oADconn = CreateObject("NameTranslate")
oADconn.Init 3, ""

Set oExcel= WScript.CreateObject("excel.application")
With oExcel
 .Visible = True
 .Workbooks.Add
 .Range("A1:C1").Select
 .Selection.Font.Bold = True
 .Cells(1,1).Value = "Display Name"
 .Cells(1,2).Value = "sAMAccountName"
 .Cells(1,3).Value = "UPN Suffix"
 .ActiveSheet.range("A2").Activate
End With

Set objDomainUsers = GetObject("WinNT://" & sDomain & ",domain")
objDomainUsers.Filter = Array("User")
On Error Resume Next
For Each oUserAcct In objDomainUsers
 oADconn.Set 3, sDomain & "\" & oUserAcct.Name
 sUserDN = oADconn.Get(1)
 Set oUser = GetObject("LDAP://" & sUserDN)
 sname=oUser.sAMAccountname
 supn=oUser.userprincipalname
 x=instr(supn,"@")
 supnsuffix=mid(supn,x+1)
 oExcel.activecell.Value=oUser.displayname
 oExcel.activecell.offset(0,1).Activate
 oExcel.activecell.Value=oUser.sAMAccountName
 oExcel.activecell.offset(0,1).Activate
 oExcel.activecell.Value=supn
 oExcel.activecell.offset(0,1).Activate
 oExcel.activecell.offset(1,-3).Activate
 if supnsuffix="testlab.priv" then
 oUser.userprincipalname=sname & "@corporate.test.int"
 oUser.SetInfo
 end if
Next
oExcel.Application.Quit

Cross Forest User Lookup LDAP Query Script

January 19, 2011 Leave a comment

I had a requirement to query users over an external trust between two AD forests, so here it is:

Const ADS_SCOPE_SUBTREE = 2
Set rootDSE = GetObject("LDAP://rootDSE")
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADSDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = "SELECT * FROM 'LDAP://corporate.contoso.com/" _
"dc=corporate,dc=contoso,dc=com' WHERE objectCategory='user'"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Set objType = GetObject(objRecordSet.Fields("ADsPath").Value)
strDistinguishedName = objType.distinguishedName
wscript.echo strDistinguishedName
objRecordSet.MoveNext
Loop

Does exactly what it says on the tin.

Query Last Logon for all Active Directory Users in any Domain

January 17, 2011 Leave a comment

I had a requirement to ascertain how many users in a domain hadn’t logged on in the last 3 months:

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

' 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.
' This bias changes with Daylight Savings Time.
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 = "sAMAccountName,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
            strCN = adoRecordset.Fields("sAMAccountName").Value
            On Error Resume Next
            Set objDate = adoRecordset.Fields("lastLogon").Value
            If (Err.Number <> 0) Then
                On Error GoTo

                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#
                    dtmDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
                        + lngLow)/600000000 - lngBias)/1440
                End If
            End If
            If (objList.Exists(strCN) = True) Then
                If (dtmDate > objList(strCN)) Then
                    objList.Item(strCN) = dtmDate
                End If
            Else
                objList.Add strCN, dtmDate
            End If
            adoRecordset.MoveNext
        Loop
        adoRecordset.Close
    End If
Next

' Output latest lastLogon date for each user.
For Each strUser In objList.Keys    
    If (objList.Item(strUser) = #1/1/1601#) Then        
        Wscript.Echo strUser & ",Never"    
    Else        
        Wscript.Echo strUser & "," & objList.Item(strUser)    
    End If
Next

 ' Clean up.ado
Connection.Close

Many thanks to ADSI God: Richard Mueller for this!