Jump to content
Strawberry Orange Banana Lime Leaf Slate Sky Blueberry Grape Watermelon Chocolate Marble
Strawberry Orange Banana Lime Leaf Slate Sky Blueberry Grape Watermelon Chocolate Marble

MSFN is made available via donations, subscriptions and advertising revenue. The use of ad-blocking software hurts the site. Please disable ad-blocking software or set an exception for MSFN. Alternatively, register and become a site sponsor/subscriber and ads will be disabled automatically. 


  • Content Count

  • Donations

  • Joined

  • Last visited

Community Reputation

0 Neutral

About michael.thomas

  1. Hi Bezalel, I would agree that the procedure is more complicated than need be there are a couple of reasons for this though: 1) Credentials are not embedded in the build because we have a policy.. no domain credentials are to be stored in plain text.. anywhere.. (I added provision for the script to have credentials embedded in it however, and this has been used when rebuilding large numbers of machines outside of hours.. kinda bending policy here..) 2) Management doesn't want to fork out for storage for roaming profiles.. (C'mon.. What the hell?!!!) 3) The script is designed to request credentials/computer name by default (machines need to be labeled/tagged at build/rebuild time, so can't really be automated in a sane fashion..) 4) Drivers.. blah.. I had 3 days to knock the whole thing together.. I would have liked to build a database for PCI ID's/drivers.. but 3 days wasn't gonna cut it! 5) WSUS.. it is handled by a GPO, but do you really want to leave it up to users to keep installing updates for the next few days? The script just forces installation of ALL updates on the spot Aside from that - I think what I've put together _should_ be easier to maintain (eg. adding drivers) for whoever replaces me.. this place is run on a shoe-string, I doubt they will get someone who has the know-how to build up driver inf's for.. interesting hardware.. Whereas this process will allow them to basically extract drivers from *.zip/*.exe etc.. dump them in a folder and give it a whirl.. if something doesn't install on it's own, add the driver installer executable to the respective post_install batch file with a silent switch.. and be done with it.. Oh, on another note - do you get much trouble with multiple drivers for the same PCI ID? or are you fixing inf's by hand on conflict? Cheers, Mike
  2. I just thought I would document what I have done for a "one image to rule them all" build here - in case the information is useful to other people.. Outline of what this does: 1) Basic installation on any hardware platform (only NIC+Mass storage are included) 2) Detect machine type (desktop/laptop) and model (bios model) 3) "Fix" computer accounts in A/D (explained later on..) 4) Join domain, download drivers for model and install 5) Install software (configurable per machine) 6) Copies user profiles (if found for that computer) 7) Updates via WSUS/Windows update etc.. Why in gods name does it do this "stuff"?! I inherited this network in a rather sad state: * No standard hardware (a quick scan of the network yeilds ~60 different configurations) * No standard naming convention (Bob1, Warehouse2, Lenovo-123456) * Slow (aging hardware, domain migration, patch bloat..) * Software on many machines is non-standard (eg. alot of the workstations have one-off pieces of software installed) Now for the fun part.. <Image Path>\i386\Templates\image.sif [Unattended] UnattendMode=FullUnattended OemPreinstall=Yes TargetPath=\WINDOWS NtUpgrade=No OverwriteOemFilesOnUpgrade=No OemSkipEula=Yes DUDisable=Yes DriverSigningPolicy=Ignore ExtendOemPartition=1 ForceHALDetection=Yes Repartition=Yes OemSkipWelcome=1 UnattendSwitch=Yes OemPnPDriversPath = Drivers\Nic [GuiUnattended] AdminPassword=<password here> EncryptedAdminPassword=Yes OEMSkipRegional=1 TimeZone=290 OemSkipWelcome=1 [Identification] JoinWorkgroup=RIS_BUILD [GuiRunOnce] Command0=%SYSTEMDRIVE%\INSTALL\post_install.cmd Only relavent code is included for brevity.. so a very basic sif.. JoinWorkgroup is used because I don't want to expose passwords in the sif.. <Image Path>\$OEM$\cmdlines.txt [COMMANDS] "create_installer_account.cmd" <Image Path>\$OEM$\create_installer_account.cmd net user Installer installer /add net localgroup Administrators Installer /add net accounts /maxpwage:unlimited REGEDIT /S installer_autologon.reg EXIT <Image Path>\$OEM$\installer_autologon.reg Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] "DefaultUserName"="Installer" "DefaultPassword"="installer" "AutoAdminLogon"="1" Right, so during the GUI setup, create a local admin account called "Installer", password "installer", then patch the registry so it will log in automatically.. easy! Now here's where things get tricky.. <Image Path>\$OEM$\$1\INSTALL\post_install.cmd @echo off SETLOCAL ENABLEDELAYEDEXPANSION REM Some configuration.. SET RIS_SERVER=<WDS SERVER> SET FILE_SERVER=<FILE SERVER> REM ensure we are in the correct folder.. not really needed.. %SYSTEMDRIVE% CD \INSTALL SET INSTALLDIR=%SYSTEMDRIVE%\INSTALL REM now where were we... REM dir command used to check if we have a state, create one if not.. DIR "%INSTALLDIR%\STATE.CMD" >NUL 2>NUL|| CALL :SET_STATE FIRST_BOOT REM load known settings DIR "%INSTALLDIR%\SETTINGS.CMD" >NUL 2>NUL && CALL "%INSTALLDIR%\SETTINGS.CMD" CALL "%INSTALLDIR%\STATE.CMD" IF "%INSTALLSTATE%"=="FIRST_BOOT" ( ECHO Starting Setup.. ECHO **************** ) ELSE ( ECHO Resuming Setup.. ECHO **************** ) REM go to where we left off.. GOTO %INSTALLSTATE% REM ####################################### :FIRST_BOOT SET WSUS_ITERATION=0 CALL :SET_SETTING WSUS_ITERATION %WSUS_ITERATION% CALL :CRUDE_WAIT_FOR_NETWORK REM We aren't on the domain yet, so use a VERY limited local account on RIS server REM to retrieve information we need.. NET USE \\%RIS_SERVER%\COMPUTER_CONFIG /USER:%RIS_SERVER%\Ris_Installer R1s1nstaller /PERSISTENT:NO REM determine what type of PC we are on.. SET PLATFORM= FOR /F %%I IN ('CSCRIPT //NoLogo get_computer_type.vbs') DO SET PLATFORM=%%I REM Default to Laptop if not sure.. can't hurt too much! IF "%PLATFORM%"=="Other" SET PLATFORM=Laptop REM see if there is a configuration for this MAC address.. SET MACADDRESS= FOR /F %%I IN ('CSCRIPT //NoLogo determine_used_mac_address.vbs') DO SET MACADDRESS=%%I REM default to "default" configuration IF "%MACADDRESS%"=="" ( SET MACADDRESS=%PLATFORM% SET NAME=%COMPUTERNAME% ) ELSE ( FOR /F %%I IN ('TYPE \\%RIS_SERVER%\COMPUTER_CONFIG\%MACADDRESS%\name.txt') DO SET NAME=%%I ) REM Get list of software to install.. SET SOFTWARE= FOR /F %%i IN ('TYPE \\%RIS_SERVER%\COMPUTER_CONFIG\%MACADDRESS%\software.txt') DO SET SOFTWARE=!SOFTWARE! %%i REM Set up a "bootstrap" for subsequent boots.. REM Use "5" so model specific scripts, app install etc.. can sort before us, if they want.. echo @CALL %INSTALLDIR%\post_install.cmd>"%ALLUSERSPROFILE%\Start Menu\Programs\Startup\5.cmd" REM Save for later use.. CALL :SET_SETTING PLATFORM %PLATFORM% CALL :SET_SETTING MACADDRESS %MACADDRESS% CALL :SET_SETTING NAME %NAME% CALL :SET_SETTING SOFTWARE %SOFTWARE% ECHO Detected Platform: %PLATFORM% ECHO Using Configuration: %MACADDRESS% ECHO Computer Name: %NAME% ECHO Packages to install: %SOFTWARE% REM "Prompter.hta" attempts to: REM Join the domain REM Change computer name REM Record Credentials in registry for autologon REM Reboot REM If for whatever reason it fails to perform the domain functions REM It will inform the user of the failure, and request that they REM Manually join/rename the PC, then reboot REM Arguments are: [<CompterName> <MacAddress>] [<Username> <Password>] REM eg. IT001 ab-cd-ef-12-34 Administrator MyPassword CALL :SET_STATE CHECK_DOMAIN mshta %INSTALLDIR%\Prompter.hta %NAME% %MACADDRESS% CALL :REBOOT GOTO :EOF REM ####################################### :CHECK_DOMAIN CALL :CRUDE_WAIT_FOR_NETWORK REM Crude.. but effective.. ECHO Checking domain.. DIR \\<Domain Controller>\netlogon 2>NUL >NUL || GOTO :NOT_JOINED CALL :SET_STATE INSTALL_DRIVERS GOTO :INSTALL_DRIVERS :NOT_JOINED ECHO "ERROR: Computer is not joined to the domain" ECHO " Please resolve and press a key to continue" PAUSE CALL :REBOOT GOTO :EOF REM ####################################### :INSTALL_DRIVERS CALL :WAIT_FOR_NETWORK REM first, get drivers for current platform (or FAILSAFE) ECHO Getting drivers from %RIS_SERVER%.. CSCRIPT //NoLogo "%INSTALLDIR%\get_model_drivers.vbs" REM Install the drivers using DPInst.. ECHO Installing Drivers.. PUSHD %SYSTEMDRIVE%\Drivers DPINST.EXE /c /s /sh /lm /el /sa POPD REM run post-install script ECHO Running driver post-install script.. DIR "%INSTALLDIR%\driver_post_install.cmd" 2>NUL >NUL && CALL "%INSTALLDIR%\driver_post_install.cmd" CALL :SET_STATE SET_RESOLUTION CALL :REBOOT GOTO :EOF REM ####################################### :SET_RESOLUTION REM Change resolution - some app installers require a minimum res.. ECHO Setting resolution.. ECHO Trying 1024x768/auto "%INSTALLDIR%\QRes.exe" /x:1024 /y:768 > NUL 2>NUL ECHO Trying 1024x768/24 "%INSTALLDIR%\QRes.exe" /x:1024 /y:768 /c:24 > NUL 2>NUL ECHO Trying 1024x768/32 "%INSTALLDIR%\QRes.exe" /x:1024 /y:768 /c:32 > NUL 2>NUL CALL :SET_STATE INSTALL_SOFTWARE GOTO INSTALL_SOFTWARE GOTO :EOF REM ####################################### :INSTALL_SOFTWARE CALL :WAIT_FOR_NETWORK ECHO Installing Software.. CSCRIPT //NoLogo \\%FILE_SERVER%\applications\installers\install.js %SOFTWARE% CSCRIPT //NoLogo \\%FILE_SERVER%\applications\installers\check_install.js %SOFTWARE% || GOTO SOFTWARE_NOT_FINISHED CALL :SET_STATE COPY_USER_PROFILES GOTO COPY_USER_PROFILES :SOFTWARE_NOT_FINISHED ECHO Not all software installed correctly, rebooting for retry.. CALL :REBOOT GOTO :EOF REM ####################################### :COPY_USER_PROFILES CALL :WAIT_FOR_NETWORK ECHO Copying user profiles.. FOR %%i in (\\%RIS_SERVER%\COMPUTER_CONFIG\%MACADDRESS%\profiles\*.*) DO CALL "%%i" CALL :SET_STATE WSUS_UPDATES GOTO WSUS_UPDATES GOTO :EOF REM ####################################### :WSUS_UPDATES CALL :WAIT_FOR_NETWORK REM use 3 iterations for WSUS.. IF %WSUS_ITERATION% GEQ 3 GOTO WSUS_FINISHED SET /A WSUS_ITERATION=0%WSUS_ITERATION%+1 >NUL 2>NUL CALL :SET_SETTING WSUS_ITERATION %WSUS_ITERATION% CSCRIPT //NoLogo "%INSTALLDIR%\wsus_update.vbs" action:install mode:silent restart:1 force:1 CALL :REBOOT GOTO :EOF :WSUS_FINISHED CALL :SET_STATE CLEANUP GOTO CLEANUP GOTO :EOF REM ####################################### :CLEANUP NET USER Installer /delete REG delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /f REG delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /f DEL "%ALLUSERSPROFILE%\Start Menu\Programs\Startup\5.cmd" %SYSTEMDRIVE% CD \ RMDIR /s /q %INSTALLDIR% && shutdown -r -t 0 GOTO :EOF REM ####################################### :SET_STATE SETLOCAL SET STATE=%1 ECHO SET "INSTALLSTATE=%STATE%"> %INSTALLDIR%\STATE.CMD ENDLOCAL GOTO :EOF :SET_SETTING SETLOCAL SET SETTING=%1 SET VALUE=%2 REM just append it to the end of the file.. even if there are duplicate entries, the last REM set value will always overwrite, so it's OK.. ECHO SET "%SETTING%=%VALUE%">> %INSTALLDIR%\SETTINGS.CMD ENDLOCAL GOTO :EOF :REBOOT SETLOCAL ECHO Rebooting.. shutdown -r -t 0 ENDLOCAL GOTO :EOF :WAIT_FOR_NETWORK SETLOCAL ECHO Waiting for network.. :WAIT_FOR_NETWORK_LOOP DIR \\<Domain Controller>\netlogon >NUL 2>NUL || GOTO WAIT_FOR_NETWORK_LOOP ENDLOCAL GOTO :EOF :CRUDE_WAIT_FOR_NETWORK SETLOCAL ECHO Waiting for network.. :CRUDE_WAIT_FOR_NETWORK_LOOP PING -n 1 2>NUL| find "Reply" | find "time" >NUL || GOTO CRUDE_WAIT_FOR_NETWORK_LOOP ENDLOCAL GOTO :EOF This nasty looking batch file is basically a state machine, it calls various scripts, reboots.. and continues where it left off.. Initally the batch file sets up some basic configuration, loads previous settings (STATE.CMD and SETTINGS.CMD), then jumps to it's current "state" FIRST_BOOT Get the computer type via get_computer_type.vbs (returns either Laptop or Desktop) Get the mac address (OK.. this is a little bit of a lie.. determine_used_mac_address.vbs checks if there is a folder with the computers mac address, if present it returns the mac address.. otherwise nothing) Default mac address to computer type (explained later..) If a valid mac address is found.. get further configuration from a folder on the WDS server (name, software to install) Create a cmd file in the all users startup folder, for subsequent reboots.. Show the "prompter", which requests a username/password/computer name, "fixes" computer accounts, saves the username/password in the registry for autologon, and reboots.. CHECK_DOMAIN Try and connect to the domain controllers netlogon share, post a message if it fails.. INSTALL_DRIVERS Calls the get_mode_drivers.vbs script, which determines the computer model, then looks on the WDS server for drivers for that model, then copies them locally. This script has a "failsafe", which copies everything from driverpacks.net.. Use DPINST.EXE to install PnP drivers Note: some hardware configurations don't have drivers that install cleanly (eg. nvidia graphics cards), so the driver_post_install.cmd can be used to run driver executables to "fix" things like that.. SET_RESOLUTION Some software we use will not install if the resolution/colour depth are set too a low level.. Uses QRes to (try) change the resolution INSTALL_SOFTWARE Ug.. not gonna go into any detail on my software installation scripts here (happy to answer questions though..), needless to say, I have written a set of scripts that behave somewhat similar to apt-get/yum etc.. (eg. software is installed as a "package", packages can have dependcies etc..) COPY_USER_PROFILES Does what it says, profile backup script included below to show how it works.. WSUS_UPDATES calls the wsus_update.vbs script, then reboots.. 3 times for sanity CLEANUP Remove autologon information, remove the script "bootstrapper" from the all users profile, then self destruct Phew! hope that high level description is enough for you guys.. feel free to ask questions.. Right.. now onto the scripts themselves.. (in order of execution) <Image Path>\$OEM$\$1\INSTALL\get_computer_type.vbs strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colChassis = objWMIService.ExecQuery _ ("Select * from Win32_SystemEnclosure") dim strType For Each objChassis in colChassis For Each strChassisType in objChassis.ChassisTypes Select Case strChassisType Case 1 strType = "Other" Case 2 strType = "Other" Case 3 strType = "Desktop" Case 4 strType = "Desktop" Case 5 ' strType = "Other" Case 6 strType = "Desktop" Case 7 strType = "Desktop" Case 8 strType = "Laptop" Case 9 strType = "Laptop" Case 10 strType = "Laptop" Case 11 strType = "Laptop" Case 12 ' strType = "Other" Case 13 ' strType = "Other" Case 14 strType = "Laptop" Case 15 ' strType = "Other" Case 16 ' strType = "Other" Case 17 strType = "Desktop" Case 18 ' strType = "Other" Case 19 ' strType = "Other" Case 20 ' strType = "Other" Case 21 ' strType = "Other" Case 22 ' strType = "Other" Case 23 strType = "Desktop" Case 24 strType = "Desktop" Case Else strType = "Unknown" End Select Next Next WScript.echo strType Fairly straight forward.. look at the ChassisType(s) for the computer, then spit it out to stdout.. <Image Path>\$OEM$\$1\INSTALL\determine_used_mac_address.vbs strComputer = "." set objFSO = Wscript.CreateObject("Scripting.FileSystemObject") Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery _ ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = True") For Each objItem in colItems strMac = Replace(objItem.MACAddress,":","-") if objFSO.FolderExists("\\<WDS SERVER>\COMPUTER_CONFIG\" & strMac) then Wscript.Echo strMac Wscript.quit end if Next Iterate through each MAC address the computer has, check if \\<WDS SERVER>\COMPUTER_CONFIG\<Mac Address> exists and spit it to stdout, otherwise nothing.. <Image Path>\$OEM$\$1\INSTALL\Prompter.hta <html> <head> <HTA:APPLICATION ID="objInstaller" APPLICATIONNAME="Installer" SCROLL="no" SINGLEINSTANCE="yes" WINDOWSTATE="normal" BORDER="dialog" CAPTION="Installer" > <title>Installer</title> <body> <script language="VBScript"> 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 private function readRegString(strKeyPath,strValueName) const HKEY_LOCAL_MACHINE = &H80000002 strComputer = "." Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &_ strComputer & "\root\default:StdRegProv") oReg.GetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strValue readRegString = strValue end function private sub WriteRegistry(strPath,strValue) Set objShell = CreateObject("WScript.Shell") objShell.RegWrite strPath, strValue, "REG_SZ" end sub private function dostuff() strComputerName = document.getelementbyid("computername").value strUserName = document.getelementbyid("username").value strUserPassword = document.getelementbyid("password").value if not fix_computer_account(strUserName,strUserPassword,strComputerName,strOldComputerName,strMacAddress) then exit function end if WriteRegistry "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName", "<DOMAIN>\" & strUserName WriteRegistry "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName", "<DOMAIN>" WriteRegistry "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword", strUserPassword WriteRegistry "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon", "1" if not join_domain_and_rename(strUserName,strUserPassword,strComputerName) then Msgbox "An error occured during domain configuration, you can try again with a different username/password" & vbcrlf & _ "or close this window and attempt to fix it yourself" exit function end if Set arrOS = GetObject("winmgmts:{(Shutdown)}//./root/cimv2").ExecQuery _ ("select * from Win32_OperatingSystem where Primary=true") for each objOS in arrOS objOS.Reboot() next self.close end function function fix_computer_account(strUser,strPassword,strNewComputerName,strOldComputerName,strMacAddress) Set objShell = CreateObject("WScript.Shell") strCmd1 = objShell.ExpandEnvironmentStrings("%INSTALLDIR%") & "\RunasPro.exe " & _ strUser & "@DOMAIN.COM " & strPassword & " c:\" strCmd2 = """cscript " + objShell.ExpandEnvironmentStrings("%INSTALLDIR%")+"\fix_compter_account.vbs" strCmd2 = strCmd2 & " " & strOldComputerName & " " & strNewComputerName & " " & strMacAddress & """" objShell.Run strCmd1 & " " & strCmd2, 0, True fix_computer_account = true end function function join_domain_and_rename(strUser,strPassword,strNewCompterName) 'Right, here's where things get.. interesting 'WScript doesn't appear to support binding to LDAP using alternate 'Credentials, unless you can bind to it via NTLM first! (unless.. 'presumably.. you mess with the premissions in AD.. let's not.. 'So.. what the hell do we do?! 'We CAN connect to another PC's WMI service, supplying credentials.. 'So.. we proxy the compter account "fudging" through it! what a task! join_domain_and_rename = True strDomain = "DOMAIN.COM" Set objNetwork = CreateObject("WScript.Network") strComputer = objNetwork.ComputerName Set objWMIService = GetObject ("winmgmts:" & "!\\" & strComputer & "\root\cimv2") Set objComputer = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & _ strComputer & "\root\cimv2:Win32_ComputerSystem.Name='" & _ strComputer & "'") ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, strPassword, strDomain & "\" & strUser, "OU=Workstations,DC=DOMAIN,dc=COM", _ JOIN_DOMAIN) if ReturnValue <> 0 then ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, strPassword, strDomain & "\" & strUser, "OU=Workstations,DC=DOMAIN,dc=COM", _ JOIN_DOMAIN + ACCT_CREATE) end if If ReturnValue <> 0 Then MsgBox "Computer not added to domain successfully. Return value: " & ReturnValue join_domain_and_rename = false exit function End If if lcase(strNewCompterName) <> lcase(strCompter) then strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colComputers = objWMIService.ExecQuery _ ("Select * from Win32_ComputerSystem") For Each objComputer in colComputers ErrCode = objComputer.Rename(strNewCompterName, strPassword, strUser) If ErrCode <> 0 Then MsgBox "Eror changing computer name. Error code: " & ErrCode join_domain_and_rename = false exit function End If Next end if end function Dim strComputerName,strUserName,strUserPassword Dim strOldComputerName, strNewComputerName, strMacAddress strOldComputerName = readRegString("SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName","ComputerName") arrCommands = Split(objInstaller.commandLine, " ") if UBound(arrCommands) > 1 then strNewComputerName = arrCommands(1) strMacAddress = arrCommands(2) else strNewComputerName = strOldComputerName strMacAddress = "FFAAFFAAFFAAFFAAFFAA" 'Dummy mac.. shouldn't cause trouble unless there 'is a computer account in AD called "ITFFAAFFAAFFAAFFAAFFAA".. end if if UBound(arrCommands) = 4 then strUserName = arrCommands(3) strUserPassword = arrCommands(4) else strUserName = "Administrator" strUserPassword = "" end if Dim s s = "<tr><td>Computer Name</td>" s = s + "<td><input id=""computername"" type=""text"" value=""" & strNewComputerName & """></td></tr>" s = s + "<tr><td>Username</td><td><input id=""username"" type=""text"" value=""" & strUserName & """></td></tr>" s = s + "<tr><td>Password</td><td><input id=""password"" type=""password"" value="""&strUserPassword&"""></td></tr>" document.write "<table>" document.write s document.write "</table>" if Len(strUserPassword) > 0 then dostuff end if </script> <input type="button" name="go" value="Install" onclick="dostuff()"> </body> </html> Display HTML page with three inputs (computername, username, password), When "Install" is clicked, do the following: 1) Put the username/password into registry for autologon 2) Attempt to "fix" computer accounts in AD (explained below) 3) Attempt to join the domain 4) Attempt to rename the computer account Now.. 1, 3 and 4 are easy! 2.. not so easy.. The problem is, the script must run under a domain account in order for it to connect to AD, and fix "stuff.." As the computer is not yet a member of the domain (and this "fixing" needs to happen prior to it joining the domain..), I created a program called "RunasPro", Which is similar to "RunAs", except you can supply a password as a parameter, and it does not do any authentication (eg. you can run xyz.exe as UNTRUSTEDDOMAIN\UnknownUser happily..) RunasPro.cpp #define UNICODE #define _WIN32_WINNT 0x0500 #include <windows.h> #include <stdio.h> #include <userenv.h> void DisplayError(LPWSTR pszAPI) { LPVOID lpvMessageBuffer; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpvMessageBuffer, 0, NULL); // //... now display this string // wprintf(L"ERROR: API = %s.\n", pszAPI); wprintf(L" error code = %d.\n", GetLastError()); wprintf(L" message = %s.\n", (LPWSTR)lpvMessageBuffer); // // Free the buffer allocated by the system // LocalFree(lpvMessageBuffer); ExitProcess(GetLastError()); } int main(int argc, WCHAR *argv[]) { HANDLE hToken; PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; WCHAR szUserName[1024]; WCHAR szPassw0rd[1024]; WCHAR szCommand[1024]; WCHAR szWorkingDirectory[1024]; si.cb = sizeof(STARTUPINFO); if (argc != 5) { printf("Usage: %s [user@domain] [password] [working directory] [cmd] ", (char*)argv[0]); printf("\n\n"); return 1; } MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,(char*)argv[1],-1,szUserName,sizeof(szUserName)/sizeof(szUserName[0])); MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,(char*)argv[2],-1,szPassw0rd,sizeof(szPassw0rd)/sizeof(szPassw0rd[0])); MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,(char*)argv[3],-1,szWorkingDirectory,sizeof(szWorkingDirectory)/sizeof(szWorkingDirectory[0])); MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,(char*)argv[4],-1,szCommand,sizeof(szCommand)/sizeof(szCommand[0])); if (!CreateProcessWithLogonW(szUserName, NULL, szPassw0rd, LOGON_NETCREDENTIALS_ONLY, NULL, szCommand, 0, NULL, szWorkingDirectory, &si, π)) DisplayError(L"CreateProcessWithLogonW"); WaitForSingleObject(pi.hProcess,INFINITE); CloseHandle(hToken); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } Very straight forward.. the only important part is the CreateProcessWithLogonW part, which, when called with LOGON_NETCREDENTIALS_ONLY, allows running under any account.. <Image Path>\$OEM$\$1\INSTALL\fix_computer_account.vbs dim strOldComperName, objOldComputer dim strNewCompterName, objNewComputer dim strMacAddress, objMacAddress strOldComputerName = WScript.Arguments(0) strNewCompterName = WScript.Arguments(1) strMacAddress = WScript.Arguments(2) WScript.echo strOldComputerName WScript.echo strNewCompterName WScript.echo strMacAddress strMacAddress = "IT"&Replace(strMacAddress,"-","") set objOldComputer = FindComputer(strOldComputerName&"$") set objNewComputer = FindComputer(strNewCompterName&"$") set objMacAddress = FindComputer(strMacAddress&"$") if not objOldComputer is nothing then if not objNewComputer is nothing then if objOldComputer.adsPath <> objNewComputer.adsPath then MergeGroupMembership objNewComputer, objOldComputer DeleteComputer objNewComputer end if end if if not objMacAddress is nothing then if objOldComputer.adsPath <> objMacAddress.adsPath then MergeGroupMembership objMacAddress, objOldComputer DeleteComputer objMacAddress end if end if ResetPassword objOldComputer elseif not objNewComputer is nothing then if not objMacAddress is nothing then if objNewComputer.adsPath <> objMacAddress.adsPath then MergeGroupMembership objMacAddress, objNewComputer DeleteComputer objMacAddress end if end if set objNewComputer = RenameComputer(objNewComputer, strOldComputerName) ResetPassword objNewComputer elseif not objMacAddress is nothing then Set objMacAddress = RenameComputer(objMacAddress, strOldComputerName) ResetPassword objMacAddress end if Sub MergeGroupMembership(objComputerSource,objComputerDest) WScript.Echo "Merging: " & objComputerSource.cn & " -> " & objComputerDest.cn On Error Resume Next dim arrGroupList : arrGroupList = objComputerSource.GetEx("memberOf") For Each strGroupDN in arrGroupList Set objGroup = GetObject("LDAP://" & strGroupDN) objGroup.add objComputerDest.adsPath next On Error GoTo 0 end Sub function FindComputer(strAccountName) 'Set objRootDSE = GetObject("LDAP://RootDSE") 'strNC = objRootDSE.Get("defaultNamingContext") strNC = "<DomainController>.DOMAIN.COM" Set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADSDSOObject" objConn.Open "ADs Provider" strQuery = "(&(objectClass=computer)(samAccountName="&strAccountName&"))" strLdapQuery = "<LDAP://" & strNC & ">;" & strQuery & ";adspath;subtree" Set objRS = objConn.Execute(strLdapQuery) if Not objRS.EOF then Set objRes = GetObject (objRS.Fields(0).Value) set FindComputer = GetObject(objRes.adspath) exit function end if set FindComputer = Nothing end function Sub ResetPassword(objComputer) objComputer.SetPassword objComputer.samAccountName end sub Sub DeleteComputer(objComputer) objComputer.DeleteObject (0) End sub Function RenameComputer(objComputer,strNewName) dim strOU :strOU = Mid(objComputer.distinguishedName,InStr(objComputer.distinguishedName,",")+1) set objOU = GetObject("LDAP://" & strOU) WScript.Echo "Moving " & objComputer.adsPath & " -> " & strNewname &" : " & strOU Set RenameComputer = objOU.MoveHere(objComputer.adsPath,"CN="&strNewName) RenameComputer.Put "sAMAccountName", strNewName & "$" RenameComputer.Put "displayName", strNewName & "$" RenameComputer.Put "dNSHostName", strNewName & ".DOMAIN.COM" RenameComputer.SetInfo end Function OK uhh.. there MAY be three computer accounts associated with this computer (don't ask..), IT<Mac Address>, BOB1/WAREHOUSE1/etc, and IT#### (new naming convention) Now, each of these computer accounts may have been in security groups etc for GPOs oldComputerName is the CURRENT computer name (eg. the name the computer will join the domain as) newComputerName is the name which we will be renamed to.. macAddress is.. obvious.. So.. this script adds the OLD COMPUTER to all groups which macAddress and newComputer belong to, deletes macAddress and newComputer, and resets the password on oldComputer Or.. similar.. eg. if oldComputer doesn't exist, it will merge new+mac, rename to old, then reset.. etc.. <Image Path>\$OEM$\$1\INSTALLget_model_drivers.vbs dim strModel Set objShell = CreateObject("WScript.Shell") Set objWshScriptExec = objShell.Exec("systeminfo") Set objStdOut = objWshScriptExec.StdOut While Not objStdOut.AtEndOfStream strLine = objStdOut.ReadLine If InStr(strLine,"System Model") Then strModel = trim(split(strLine,":")(1)) End If Wend dim objFSO,objFolder set objFSO = Wscript.CreateObject("Scripting.FileSystemObject") set objFolder = objFSO.GetFolder("\\<WDS SERVER>\MODEL_SPECIFIC") strModelDirectory = "FAILSAFE" for each subfolder in objFolder.subFolders dim compareLength compareLength = len(strModel) if len(subfolder.name) < compareLength then compareLength = len(subfolder.name) end if if left(lcase(subfolder.name),compareLength) = left(lcase(strModel),compareLength) then strModelDirectory = subfolder.name end if next objShell.Run "%COMSPEC% /c echo d | xcopy /E ""\\<WDS SERVER>\MODEL_SPECIFIC\" & strModelDirectory & "\Drivers"" ""%SYSTEMDRIVE%\Drivers""",,true objShell.Run "%COMSPEC% /c copy /y ""\\<WDS SERVER>\MODEL_SPECIFIC\" & strModelDirectory & "\post_install.bat"" """ & objShell.ExpandEnvironmentStrings("%INSTALLDIR%") & "\driver_post_install.cmd""",,true OK err.. run systeminfo, read the output and parse the "System Model", then, using that model, look on the WDS folder as follows: eg. Computer Model: ABC123 Search order: ..\A ..\AB ..\ABC ..etc In other words, match as close as possible to the model name, this way, computers with different revisions (ABCv1, ABCv2, ABCv3) can be grouped into ABC! wsus_update.vbs is not included here - it was poached from: http://www.vbshf.com/?p=13 That's all for now guys - feel free to ask any questions you like!
  • Create New...