Friday, April 17, 2009

Another Way to Reset Local Passwords on Remote Computers

I should have titled this one "More ways to do more with more and more" or something dumber, but I'm just getting home on a BEAUTIFUL, warm, sunny Friday afternoon. I sat on the HRBT (that's "Hampton Roads Bridge-Tunnel" for you foreigners, or as we call it "the bridge-tunnel to nowhere") for 45 minutes while they cleared another wreck from the entrance. As always, it's someone with license plates from Maryland, Delaware, New Jersey or Pennsylvania, going to fast and thinking they can handle the sheer mind stress of seeing their wide-open view of the Hampton Roads Bay shrink to a miniscule concrete encased box of a tube entrance. Not that the entrance is really that significant, it just looks that way to those not familiar with commuting through it twice a day, every day.

Sorry. See what happens when tourists f**k up a perfectly good Friday?

Ok, let's get on with it then. Here are two versions (that's right: 2) for resetting the local "Administrator" account password on all the computers within your Active Directory domain. The first one is the "easy", quick, simple, version, without bells-and-whistles or error handling. The second one is the beefier, bloatier, buffier (is that a word?) version which lets you restrict your wild and crazy behavior to just non-server clients, or if you prefer: all clients (servers included). It also provides a basic amount of error handling, but not too crazy.

So, here's the BASIC version:

Option Explicit

Const cmdStr = "pspasswd.exe \\COMPUTER Administrator P@ssW0rd$1$2$3"
Const ADSI_DN = "mydomain.com"

'----------------------------------------------------------------
' comment: do not change any code below this point!
'----------------------------------------------------------------

Wscript.Echo "info: starting processing: " & Now
Dim objShell, objX
Set objShell = CreateObject("Wscript.Shell")
Set objDom = GetObject("WinNT://" & ADSI_DN)
For each objX in objDom
If Lcase(objX.Class) = "computer" Then
Wscript.Echo "info: connecting to " & objX.Name
objShell.Run(Replace(cmdStr, "COMPUTER", objX.Name), 1, True)
End If
Next

Wscript.Echo "info: completed processing: " & Now

And, here's the DELUXE version:

Option Explicit

Const cmdStr = "pspasswd.exe \\COMPUTER Administrator P@ssW0rd$1$2$3"
Const RunMode = "TEST"
Const QueryScope = "NO_SERVERS"

'----------------------------------------------------------------
' comment: do not change any code below this point!
'----------------------------------------------------------------

Const NTDSDSA_OPT_IS_GC = 1
Const ADS_SCOPE_SUBTREE = 2
Const ADS_UF_ACCOUNTDISABLE = 2

Dim objRootDSE, LDAP_DN, ADSI_DN, objShell, t1, t2

Wscript.Echo "info: beginning processing: " & Now
t1 = Now

Set objRootDSE = GetObject("LDAP://rootDSE")
Set objShell = CreateObject("Wscript.Shell")
LDAP_DN = Domain_LDAP()
ADSI_DN = Domain_NetBIOS(LDAP_DN)

'----------------------------------------------------------------
' comment:
'----------------------------------------------------------------

Wscript.Echo "info: ldap_dn = " & LDAP_DN
Wscript.Echo "info: adsi_dn = " & ADSI_DN

Dim objConn, objCmd, objRs
Dim retval : retval = 0
Dim strQuery, strComputer, runCmd

If QueryScope = "NO_SERVERS" Then
Wscript.Echo "info: enumerating non-server computer objects..." & vbCRLF
strQuery = "SELECT Name FROM 'LDAP://" & LDAP_DN & _
"' WHERE objectClass='computer' AND operatingSystem <> 'Windows*Server*'"
Else
Wscript.Echo "info: enumerating all computer objects..." & vbCRLF
strQuery = "SELECT Name FROM 'LDAP://" & LDAP_DN & _
"' WHERE objectClass='computer'"
End If

On Error Resume Next
Set objConn = CreateObject("ADODB.Connection")
Set objCmd = CreateObject("ADODB.Command")
objConn.Provider = "ADsDSOObject"
objConn.Open "Active Directory Provider"
Set objCmd.ActiveConnection = objConn
objCmd.CommandText = strQuery
objCmd.Properties("Page Size") = 1000
objCmd.Properties("Timeout") = 30
objCmd.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCmd.Properties("Cache Results") = False
Set objRs = objCmd.Execute
If Err.Number <> 0 Then
Set objShell = Nothing
Wscript.Echo "error: " & Err.Number & " - " & Err.Description
Wscript.Quit
End If
objRs.MoveFirst

Do Until objRs.EOF
strComputer = objRs.Fields("Name").Value
runCmd = Replace(cmdStr, "COMPUTER", strComputer)
If RunMode = "TEST" Then
Wscript.Echo "exec: " & runCmd
Else
If IsOnline(strComputer) Then
Wscript.Echo "info: connecting to " & strComputer
objShell.Run(runCmd, 1, True)
End If
End If
objRs.MoveNext
Loop

Wscript.Echo "info: completed processing: " & Now
Wscript.Echo "info: total runtime was " & Abs(DateDiff("s", t1, t2)) & " seconds"

Set objShell = Nothing

'----------------------------------------------------------------
' function: query for domain LDAP-FQDN (e.g. "dc=contoso,dc=msft")
'----------------------------------------------------------------

Function Domain_LDAP()
Domain_LDAP = objRootDSE.Get("defaultNamingContext")
End Function

'----------------------------------------------------------------
' function: query for domain NetBIOS suffix (e.g. "contoso.msft")
'----------------------------------------------------------------

Function Domain_NetBIOS(ldapdn)
Domain_NetBIOS = Replace(Replace(ldapdn,"DC=",""),",",".")
End Function

'----------------------------------------------------------------
' function: verify computer is accessible over the network
'----------------------------------------------------------------

Function IsOnline(strComputer)
Dim objPing, objStatus, retval, strQuery
strQuery = "select * from Win32_PingStatus where address = '" & strComputer & "'"
If strComputer <> "" Then
Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}")._
ExecQuery(strQuery)
For Each objStatus in objPing
If Not(IsNull(objStatus.StatusCode)) And objStatus.StatusCode = 0 Then
IsOnline = True
End If
Next
End If
End Function


Well, enjoy your weekend! I hope these are helpful, useful or at least mildly entertaining to you. If you have questions, comments or good jokes to share, just drop a comment via the feedback. Thanks!

1 comment:

skatterbrainz said...

Updated the code in the "IsOnline()" function to shorten it by changing the default condition test from False to make it True. Eliminates a whopping 2 massive lines of code! Wow! :)