19 December 2010

Automating Desktop Installation PT3. The main event

A quick recap then. In the previous sessions we have created a list of MAC addresses for all our machines (called macs.txt). We have one machine which has all our software installed which we are going to clone. We (optionally) installed Mike Lin's startup-cpl control panel applet and we have created two encrypted password files called lcl.txt and net.txt.

The first thing to do now is make sure that the macs.txt file is copied into the postghost\hostname folder so do that now.

Next we need to create the script which does the renaming and domain joining. So lets get on with it. Here is the code to do this. Copy it and save the file in the postghost\hostname folder.

'
' Set local admin a/c details here
'
Username = "Administrator"
Password = ""
strDomain = "mydomain.com" 'change this to your own domain
strUser = "mydomain\myusername" 'change this to your domain admin username
strPassword = ""
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Identify the hostname by finding from a list of mac addresses
'
dim maclist(8) '*shouldn't need more than this - increase if more than 8 nic's
dim macount
dim macsfile
dim strMac,strHName
dim eqpos
Dim sbox(255)
Dim key(255)
Dim fso
Dim tst
Dim Oput,strReadin,strAdminPwd,strUsrPwd
Const JOIN_DOMAIN = 1
Const ACCT_CREATE = 2
Const ACCT_DELETE = 4
Const WIN9X_UPGRADE = 16
Const DOMAIN_JOIN_IF_JOINED = 32
Const JOIN_UNSECURE = 64
Const MACHINE_PASSWORD_PASSED = 128
Const DEFERRED_SPN_SET = 256
Const INSTALL_INVOCATION = 262144
plaintxt = "YouAreNotATerminatorRobot" 'text to use as common key

Set fso = createObject("Scripting.FileSystemObject")
Set tst = fso.OpenTextFile("lcl.txt", 1, false) 'mode2=write (append=8) - output file
'read first
While Not tst.AtEndOfStream
strReadin = tst.readLine
wend
if strReadin <>"" then
strAdminPwd = EnDeCrypt(strReadin, plaintxt)
Password = strAdminPwd
end if

Set objWMIService = GetObject("winmgmts:\\127.0.0.1\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = True")
macount=1
For Each ObjItem in colItems
maclist(macount) = objItem.MACAddress
macount = macount+1
Next
'uncomment to check first mac address is returned
'WScript.Echo "First mac =" & maclist(1)

'begin search for matching mac
Set fso = createObject("Scripting.FileSystemObject")
Set macsfile = fso.OpenTextFile("Macs.txt", 1, false) 'mode2=write (append=8)
While Not macsfile.AtEndOfStream
strMac = macsfile.readLine
if right(strMac,17)=maclist(1) Then
eqpos=instr(strMac,"=")
if eqpos<>0 then
strHName=left(strMac,(eqpos-1))
'WScript.Echo "Found match " & maclist(1) & " = " & strHName
'Now know the hostname so rename here
Set objWMIService = GetObject("Winmgmts:root\cimv2")
' Call always gets only one Win32_ComputerSystem object.
For Each objComputer in _
objWMIService.InstancesOf("Win32_ComputerSystem")
Return = objComputer.rename(strHName,Password,Username)
If Return <> 0 Then
WScript.Echo "Rename failed. Error = " & Err.Number
Else
'WScript.Echo "Rename succeeded." & " Reboot for new name to go into effect"
End If
Next
'end of renaming code

end if
End if
Wend

Set tst = fso.OpenTextFile("c:\postghost\joindom\net.txt", 1, false) 'mode2=write (append=8) - output file
'read second
While Not tst.AtEndOfStream
strReadin = tst.readLine
wend
if strReadin <>"" then
strUsrPwd = EnDeCrypt(strReadin, plaintxt)
strPassword = strUsrPwd
end if

'now join domain
Set objNetwork = CreateObject("WScript.Network")
strComputer = objNetwork.ComputerName

Set objComputer = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & strComputer & "\root\cimv2:Win32_ComputerSystem.Name='" & strComputer & "'")

ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, strPassword, strUser, NULL, JOIN_DOMAIN + ACCT_CREATE)
'ok so reboot
Set OpSysSet = GetObject("winmgmts:{(Shutdown)}//./root/cimv2").ExecQuery("select * from Win32_OperatingSystem where Primary=true")
for each OpSys in OpSysSet
OpSys.Reboot()
next


Again you need to add Mike Shaffers RC4 encrypt/decrypt routines to the end of this code. The two routines required are:

Sub RC4Initialize(strPwd) &
Function EnDeCrypt(plaintxt, psw)

Also don't forget to change the plaintext keyword (below the const lines) if you changed this in the password encryption routines from the previous posts.

So that should be it. Save your script file as hostname.vbs and then drop it onto the run-once tab in startup-cpl. Shutdown your machine and clone the drive when you're ready.

At this point it is worth pointing out that there are usually other applications which require some work after cloning. Kaspersky antivirus for example (at least our enterprise version) requires a command line to individualise it which is:

c:\progra~1\kasper~1\networ~1\klmover.exe -dupfix

Other applications like SPSS require you to re-run the license utility after the hostname has changed. We discovered we could copy an older version of spssactivator.exe into our postghost folder and use the following batch file to do this:

copy spssactivator.exe "c:\program files\SPSSInc\PASWStatistics18\spssactivator.exe"
cd /d "c:\program files\SPSSInc\PASWstatistics18"
start "SPSS18Act" "c:\program files\SPSSInc\PASWStatistics18\spssactivator.exe" (add license key here)

One day I fully expect software companies to keep track of all this via their own cloud-based licensing servers. For now, the very best of luck and if you wish to express gratitude for all the work presented here, please make a donation to the charity of your choice to help make the world a little bit better.

Update
======
I also forgot to mention that I didn't include code to remove the machines from the domain if they already exist. My solution was to remove the machines before cloning them using the active directory users & computers utility. If they're not there already, they can't cause a problem. :)