none
[分享]對基於 WDS 部署客戶端重命名方法的更新 RRS feed

  • 常规讨论

  • 本文對基於 Windows XP 的重新啓用 cmd 方法的詳細描述,更主要的是對重命名計算機方法的更新,因時間和精力限制,目前隻在 Windows XP 進行了測試, NT 6 系統理論上不存在大問題。

    之前的兩個相關帖子

    [分享]幾個關於 WDS 遠程部署的方法
    http://social.technet.microsoft.com/Forums/zh-CN/ac346be7-3a90-440e-84c7-1c5b98021770/-wds-?forum=windowsserversystemzhchs

    [分享]對之前通過 WDS 部署 Windows XP 映像文件方法的一些更新
    http://social.technet.microsoft.com/Forums/zh-CN/1bb03676-6a6e-49b3-af2f-82a23668bfaa/-wds-windows-xp-?forum=windowsserversystemzhchs

    此方法由

     一個 cmd.exe 文件副本
     cmd 的映像調試器
     兩個自定義系統“服務”
     三個 VBS 脚本
     四個批處理脚本

    組成。

    首先爲了方便描述相關脚本位于祇能由 Administrators 組和 SYSTEM 訪問的

    %WINDIR%\tmp

    目錄下。

    1. cmd.exe 的文件副本。

    在 %WINDIR%\system32 目錄複製一個 cmd.exe 文件的副本 xcmd.exe

    2.  cmd 的映像調試器

    内容如下

    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\soft\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\cmd.exe]
    "Debugger"="xcmd /c @echo [debug] %date% %time% %* >> %WINDIR%\\tmp\\xcmd.log"
    

    内容如上

    其中

    HKEY_LOCAL_MACHINE\soft

    爲參考計算機的注冊表項 SOFTWARE 在加載而來。該功能禁用 cmd 執行並將傳入的命令行記錄到日誌文件中,

    更準確說是在 Windows 安裝程序運行期間阻止通過 Shift+F10 調出以 SYSTEM 賬戶身份運行的 cmd。

    3. 兩個自定義系統“服務”

    這兩個系統“服務”並非真正意義上的服務,祇是利用其運行在 SYSTEM 賬戶下,並可以設置爲自動運行的兩個脚本。

    a. 第一個服務 foosvr

    foosvr 的設置來自於系統服務 scardsvr。用於執行解除 cmd 運行限制。

    其映像文件路徑被修改爲

    xcmd /c cscript -nologo -b %WINDIR%\tmp\EnableCMDRequest.vbs & @echo [debug] %date% %time% foosvr >> %WINDIR%\tmp\foosvr.log

    此外其啓動類型修改爲“自動啓動”,顯示名和描述修改爲 “Foo Service”,啓動賬戶改爲 LocalSystem,剩餘項不變。

    選用 scardsvr 服務的理由,因爲其根據注冊表項

    HKLM\SYSTEM\CurrentControlSet\control\ServiceGroupOrder

    的 List 值看到所屬組 SmartCardGroup 在 netlogon 服務的 RemoteValidation 之前,但要晚於在 Windows 安裝程序尚未退出就開始執行的所屬于 SpoolerGroup 的服務。

    關於 ServiceGroupOrder 的文章

    Automatically Starting Services
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms681957(v=vs.85).aspx

    雖然從實際情況看 netlogon 服務實際要略早於 scardsvr 服務,但實際看並無什麽影響。

    其實上祇要能保證在 Windows 安裝程序結束運行之後再執行就可以了,即便在 cmd 重新啓用前有未能執行的批處理脚本。也可以用再次執行的方法來解決。比如第一個帖子所提到的使用域的開機脚本。

    需要説明的是,並未測試能否通過工作組的開機脚本實現相同功能,感興趣的可以自行測試。

    b. 第二個服務 reboot

    顧名思義其用途爲重啓計算機。爲了保證其能完成重啓被設置爲強制重啓。

    它的設置同樣源自 scardsvr 服務。

    因爲重啓祇在需要時才執行,所以其啓動方式被修改爲“手動”,顯示名和描述修改爲 “Reboot Service”,啓動賬戶改爲 LocalSystem。其映像文件路徑爲

    C:\WINDOWS\System32\cmd.exe /c start "" C:\WINDOWS\tmp\reboot.bat 

    因爲本來不是一個正常的服務,響應超時的話會被強制終止,所以用 cmd /c 來保證脚本 reboot.bat 不被強制終止。

    本來在之前的方法中,重啓系統都是由具有重啓權限的用戶直接調用 shutdown 執行,但因爲一些原因改爲用服務來進行。至於原因在介紹的 reboot.bat 時會進行説明。

    3. 三個 VBS 脚本

    a. 第一個 VBS 脚本 EnableCMDRequest.vbs

    該脚本命名參照 NT 6 系列用於在 Windows 安裝程序中按 Shift+F10 禁用 cmd 的 DisalbeCMDRequest.TAG。

    代碼如下

    Dim sh, para, errCode
    set sh = CreateObject("WScript.Shell")
    para = ""
    errCode = -1
    para = "xcmd.exe /c (xcmd /c set | find '__PROCESS_HISTORY=%WINDIR%\system32\setup.exe' 2> nul 1> nul) || (reg delete 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\cmd.exe' /f && xcmd /c @echo [debug] %date% %time% reg_delete_cmd 0 >> %WINDIR%\tmp\debug.log)"
    para = replace(para, "'", chr(34))
    errCode = sh.run(para, 0, true)
    para = "%WINDIR%\tmp\create_task.bat"
    errCode = -1
    errCode = sh.run(para, 0, false)

    代碼如上

    b. 第二個 VBS 脚本 timeout.vbs

    在之前的方法中,發現在不使用 timeout.exe 或 sleep.exe,而祇用 ping 難以保證延時準確。

    代碼如下

    Wscript.Sleep Wscript.Arguments(0) * 1000

    代碼如上

    參考至:

    Sleeping in a batch file
    http://stackoverflow.com/questions/166044/sleeping-in-a-batch-file

    c. 第三個 VBS 脚本 rename.vbs

    這個脚本用來代替 wmic computersystem 的 rename 方法,因爲在測試中發現 wmic 可能會在執行過程中存在交互式提示,而其請求提供的内容未知,解決方法也暫不可知。

    所以這裡將 MSDN 的示例代碼進行了修改

    代碼如下

    Dim Name, Username, Password, objComputer, ReturnCode
         
    On Error Resume Next
    
    Name = WScript.Arguments(0)
    Username = WScript.Arguments(1)
    Password = WScript.Arguments(2)
    ReturnCode = 1
    
    Set objWMIService = GetObject("Winmgmts:root\cimv2")
    
    ' Call always gets only one Win32_ComputerSystem object.
    For Each objComputer in _
        objWMIService.InstancesOf("Win32_ComputerSystem")
    
     ReturnCode = objComputer.rename(Name,Password,Username)   
     If Err.Number <> 0 Then
      ReturnCode = 1
     End If
     
     WScript.Echo ReturnCode
    Next
    

    代碼如上

    參考至:

    Rename method of the Win32_ComputerSystem class
    http://msdn.microsoft.com/en-us/library/aa393056(v=vs.85).aspx

    3. 四個批處理脚本

    a. 第一個批處理脚本 create_task.bat

    該脚本用於重命名的初始動作。

    代碼如下

    @echo off
    
    del %WINDIR%\tmp\EnableCMDRequest.vbs /a/f/q
    sc delete foosvr
    
    schtasks -delete -tn exec_task -f
    schtasks -create -tn exec_task -tr %WINDIR%\tmp\exec_task.bat -sc onstart -ru system
    schtasks -run -tn exec_task
    
    del "%0" /a/f/q
    

    代碼如上

    b. 第二個批處理脚本 exec_task.bat

    該脚本用於執行自動重命名計算機任務, 也包括一些設置操作。比如禁用磁盤寫入緩存功能。
    還有最後的清理任務。

    代碼如下

    @echo off
    
    if exist %WINDIR%\tmp\TaskSeq.lck (
     echo [debug] %date% %time% lock >> %WINDIR%\tmp\exec.log
     goto end
    )
    
    set /p=<nul > %WINDIR%\tmp\TaskSeq.lck
    
    ver | find /i "5." > nul
    
    if %errorlevel% neq 0 (
     echo [debug] %date% %time% version >> %WINDIR%\tmp\exec.log
     goto exit
    )
    
    del !WINDIR!\tmp\EnableCMDRequest.vbs /a/f/q
    sc delete foosvr
    
    for /f "tokens=*" %%n in ('reg query "HKLM\SYSTEM\CurrentControlSet\Enum\IDE" /s ^| findstr /i/r/c:"HKEY_.*\\IDE\\Disk.*Device Parameters$" 2^> nul') do (
     @reg add "%%n\Disk" /v UserWriteCacheSetting /t REG_DWORD /d 0x0 /f
    )
    
    reg import %WINDIR%\tmp\c
    reg import %WINDIR%\tmp\t
    del %WINDIR%\tmp\c /a/f/q
    del %WINDIR%\tmp\t /a/f/q
    
    net time /setsntp:contoso.com,0x1
    
    set nic=本地连接
    set cname=CLI
    set ip=
    set gateway=
    set isDone=false
    set username=contoso\helper
    set password=PASSWORD
    set returncode=1
    set /a retrycount=0
    set /a MAX_RETRY_COUNT=60
    set /a TIMEOUT=5
    set /a CSH_TIMEOUT=120
    
    setlocal enabledelayedexpansion
    for /f "tokens=2 delims=:" %%n in ('ipconfig ^| find /i "ip address"') do (
     set ip=%%n
     set ip=!ip:~1!
     for /f "tokens=1" %%i in ('echo !ip!') do set ip=%%i
    )
    
    echo !ip! | findstr /r/i/c:"^169\.254" 2> nul 1> nul
    if !errorlevel! equ 0 (
     echo [debug] !date! !time! ip >> !WINDIR!\tmp\exec.log
     ipconfig -release !nic!
     ipconfig -renew !nic!
     goto exit
    )
    
    for /f "tokens=1-4 delims=." %%a in ('echo !ip!') do (
     set gateway=%%a.%%b.%%c.1
     set cname=%cname%%%c-%%d
    )
    
    for /f "tokens=*" %%n in ('hostname') do (
     if /i %%n equ !cname! (
      set isDone=true
     )
    )
    
    if !isDone! equ true (goto cleanup)
    
    diskpart -? > nul
    
    if !errorlevel! neq 0 (
     echo [debug] !date! !time! privilege >> !WINDIR!\tmp\exec.log
     goto exit
    )
    
    :loop
    for /f "tokens=1" %%c in ('cscript -nologo -t:!CSH_TIMEOUT! "!WINDIR!\tmp\rename.vbs" "!cname!" "!username!" "!password!"') do (
     set /a retrycount=!retrycount!+1
     set returncode=%%c
    
     if !returncode! equ 0 (
      echo [debug] !date! !time! done >> !WINDIR!\tmp\exec.log
      set isDone=true
      goto cleanup
     ) else (
      echo [debug] !date! !time! wmierr_%%c >> !WINDIR!\tmp\exec.log
      set isDone=false  
     )
    )
    cscript -nologo -b !WINDIR!\tmp\timeout.vbs !TIMEOUT!
    if !retrycount! lss !MAX_RETRY_COUNT! (
     goto loop
    ) else (
     sc start reboot
     goto exit
    )
    
    netsh int ip set addr "!nic!" static !ip! 255.255.255.0 !gateway! 1
    netsh int ip set dns "!nic!" static 192.168.100.4 primary
    netsh int ip add dns "!nic!" 192.168.100.7 index=2
    
    :cleanup
    
    if !isDone! equ true (
     reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v legalnoticecaption /t REG_SZ /d "" /f
     reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v legalnoticetext /t REG_SZ /d "" /f
    
     set /p=<nul > !WINDIR!\tmp\DONE
     del !WINDIR!\tmp\TaskSeq.lck /a/f/q
     schtasks -delete -tn remove_helper -f
     schtasks -create -tn remove_helper -tr %WINDIR%\tmp\remove_helper.bat -sc onstart -ru system
     sc start reboot
     endlocal
     del "%0" /a/f/q
    )
    
    :exit
    del %WINDIR%\tmp\TaskSeq.lck /a/f/q
    endlocal
    
    :end
    

    代碼如上

    此代碼會嘗試重命名計算機制定次數,當存在臨時網絡障礙,例如 switch 沒有啓動 portfast 功能時,此方法有助於提高重命名的成功率。

    對 portfast 的相關 Microsoft 支持

    Event ID 5719 is logged when you start a Domain Member
    http://support.microsoft.com/kb/938449

    b. 第三個批處理脚本 remove_helper.bat

    這個脚本用於清理之前所用到的所有脚本,包括可能沒有被清理的脚本。

    代碼如下

    @echo off
    
    if not exist %WINDIR%\tmp\DONE (goto exit)
    
    sc delete foosvr
    sc delete reboot
    del %WINDIR%\tmp\reboot.bat /a/f/q
    
    del "%WINDIR%\tmp\*.vbs" /a/f/q
    
    schtasks -delete -tn exec_task -f
    schtasks -delete -tn remove_helper -f
    del %WINDIR%\tmp\create_task.bat /a/f/q
    del %WINDIR%\tmp\exec_task.bat /a/f/q
    del "%0" /a/f/q
    
    :exit

    代碼如上

    d. 第四個批處理脚本 reboot.bat

    這個脚本用於強制重啓計算機。

    在測試中,發現如果當按 Ctrl+Alt+Delete 後,出現輸入用戶名和密碼的登錄對話框,此時調用 shutdown 命令會被系統阻止。

    所以編寫了這個脚本,在足夠長的時間不斷嘗試強制重啓計算機。

    代碼如下

    @echo off
    
    shutdown -a
    shutdown -a
    
    set /a MAX_RETRY_COUNT=7200
    set /a TIMEOUT=5
    set /a PAUSE=1
    
    setlocal enabledelayedexpansion
    for /l %%i in (1,1,!MAX_RETRY_COUNT!) do (
     rundll32.Exe user32.dll LockWorkStation
     echo [debug] !date! !time! shutdown >> !WINDIR!\tmp\shutdown.log
     shutdown -r -t !TIMEOUT! -f -c "系统即将强制重新启动。" | find /c /v "" | find "0" > nul
     if !errorlevel! equ 0 (  
      echo [debug] !date! !time! exit >> !WINDIR!\tmp\shutdown.log
      goto exit
     )
     cscript -nologo -b !WINDIR!\tmp\timeout.vbs !PAUSE!
    )
    
    :exit
    endlocal 
    
    
    
    

    代碼如上

    雖然在目前的方法中,依然使用用服務重啓的方法,但任務計劃都是以 SYSTEM 賬戶身份執行,所以可以考慮直接用過 cmd /c reboot.bat 方式來重啓。


    Folding@Home

    2014年3月16日 14:00

全部回复

  • 昨天發佈的 exec_task.bat 在判斷脚本是否已經在運行的方法不是很好,存在問題。

    把其改爲利用 Windows 進程在運行時不能替換自身映像文件的特性,來判斷是否已經在運行。

    代碼如下

    @echo off
    
    echo f | xcopy /hrky %WINDIR%\system32\winver.exe %WINDIR%\tmp\exec_task_lock.exe > nul
    if %errorlevel% neq 0 (
    	echo [debug] %date% %time% lock >> %WINDIR%\tmp\exec.log
    	goto end
    ) else (
    	 start "" /min %WINDIR%\tmp\exec_task_lock.exe
    )
    
    ver | find /i "5." > nul
    
    if %errorlevel% neq 0 (
    	echo [debug] %date% %time% version >> %WINDIR%\tmp\exec.log
    	goto exit
    )
    
    cscript -nologo -b %WINDIR%\tmp\timeout.vbs 120
    
    del !WINDIR!\tmp\EnableCMDRequest.vbs /a/f/q
    sc delete foosvr
    
    for /f "tokens=*" %%n in ('reg query "HKLM\SYSTEM\CurrentControlSet\Enum\IDE" /s ^| findstr /i/r/c:"HKEY_.*\\IDE\\Disk.*Device Parameters$" 2^> nul') do (
    	@reg add "%%n\Disk" /v UserWriteCacheSetting /t REG_DWORD /d 0x0 /f
    )
    
    
    reg import %WINDIR%\tmp\c
    reg import %WINDIR%\tmp\t
    del %WINDIR%\tmp\c /a/f/q
    del %WINDIR%\tmp\t /a/f/q
    
    net time /setsntp:contoso.com,0x1
    netsh firewall set icmp 8 enable all
    
    set nic=本地连接
    set cname=CLI
    set ip=
    set gateway=
    set isDone=false
    set username=contoso\helper
    set password=PASSWORD
    set returncode=1
    set /a retrycount=0
    set /a MAX_RETRY_COUNT=60
    set /a TIMEOUT=5
    set /a CSH_TIMEOUT=120
    
    setlocal enabledelayedexpansion
    for /f "tokens=2 delims=:" %%n in ('ipconfig ^| find /i "ip address"') do (
    	set ip=%%n
    	set ip=!ip:~1!
    	for /f "tokens=1" %%i in ('echo !ip!') do set ip=%%i
    )
    
    echo !ip! | findstr /r/i/c:"^169\.254" 2> nul 1> nul
    if !errorlevel! equ 0 (
    	echo [debug] !date! !time! ip >> !WINDIR!\tmp\exec.log
    	ipconfig -release !nic!
    	ipconfig -renew !nic!
    	goto exit
    )
    
    for /f "tokens=1-4 delims=." %%a in ('echo !ip!') do (
    	set gateway=%%a.%%b.%%c.1
    	set cname=%cname%%%c-%%d
    )
    
    for /f "tokens=*" %%n in ('hostname') do (
    	if /i %%n equ !cname! (
    		set isDone=true
    	)
    )
    
    if !isDone! equ true (goto cleanup)
    
    diskpart -? > nul
    
    if !errorlevel! neq 0 (
    	echo [debug] !date! !time! privilege >> !WINDIR!\tmp\exec.log
    	goto exit
    )
    
    :loop
    for /f "tokens=1" %%c in ('cscript -nologo -t:!CSH_TIMEOUT! "!WINDIR!\tmp\rename.vbs" "!cname!" "!username!" "!password!"') do (
    	set /a retrycount=!retrycount!+1
    	set returncode=%%c
    
    	if !returncode! equ 0 (
    		echo [debug] !date! !time! done >> !WINDIR!\tmp\exec.log
    		set isDone=true
    		goto cleanup
    	) else (
    		echo [debug] !date! !time! wmierr_%%c >> !WINDIR!\tmp\exec.log
    		set isDone=false		
    	)
    )
    cscript -nologo -b !WINDIR!\tmp\timeout.vbs !TIMEOUT!
    if !retrycount! lss !MAX_RETRY_COUNT! (
    	goto loop
    ) else (
    	sc start reboot
    	goto exit
    )
    
    netsh int ip set addr "!nic!" static !ip! 255.255.255.0 !gateway! 1
    netsh int ip set dns "!nic!" static 192.168.100.4 primary
    netsh int ip add dns "!nic!" 192.168.100.7 index=2
    
    :cleanup
    
    if !isDone! equ true (
    	reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v legalnoticecaption /t REG_SZ /d "" /f
    	reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v legalnoticetext /t REG_SZ /d "" /f
    
    	set /p=<nul > !WINDIR!\tmp\DONE
    	schtasks -delete -tn remove_helper -f
    	schtasks -create -tn remove_helper -tr !WINDIR!\tmp\remove_helper.bat -sc onstart -ru system
    	taskkill -im exec_task_lock.exe -f
    	sc start reboot
    	endlocal
    	del "%0" /a/f/q
    )
    
    :exit
    
    endlocal
    taskkill -im exec_task_lock.exe -f
    :end
    

    代碼如上

    以及在 remove_helper.bat 中的修改

    代碼如下

    @echo off
    
    if not exist %WINDIR%\tmp\DONE (goto exit)
    
    sc delete foosvr
    sc delete reboot
    del %WINDIR%\tmp\reboot.bat /a/f/q
    
    del "%WINDIR%\tmp\*.vbs" /a/f/q
    
    schtasks -delete -tn exec_task -f
    schtasks -delete -tn remove_helper -f
    del %WINDIR%\tmp\create_task.bat /a/f/q
    del %WINDIR%\tmp\exec_task.bat /a/f/q
    del %WINDIR%\tmp\exec_task_lock.exe /a/f/q
    del "%0" /a/f/q
    
    :exit
    

    代碼如上


    Folding@Home

    2014年3月17日 15:28

  • 再次感谢精彩的分享!


    Jeremy Wu

    TechNet Community Support

    2014年3月18日 15:56
    版主
  • 對自動部署 Windows 客戶端的更新説明

    沒有新的内容,主要是對於不佳的網絡環境的通解決方案。

    如果當存在 portfast 設置不當,或者其他映像客戶端網絡初始化問題,導致新部署的客戶端系統不能及時應用。比如將指定安全組加入到本地組。

    1. 在初始脚本(這裡是 exec_task.bat),添加一個以 SYSTEM 權限開機運行的脚本。
    2. 該脚本大致内容如下

    以下爲代碼

    @echo off
    cscript -nologo -b %WINDIR%\system32\timeout.vbs 300
    net localgroup locgrp "CONTOSO\secgrp" /add 2>> %WINDIR%\tmp\locgrp.err 1>> %WINDIR%\tmp\locgrp.log
    
    net localgroup locgrp | find /i "CONTOSO\secgrp" > nul
    if %errorlevel% equ 0 (
            schtasks -delete -tn locgrp -f
            del "%0" /a/f/q
    )
    


    以上爲代碼

    上面的方法還不是最佳的變通方案,存在延時過長的問題,如果有更好的方法,將進行更新。

    在 “[分享] 幾個關於 WDS 遠程部署的方法”提到的在脚本 loader.bat 搜尋存放 WIM/SWM 映像文件更好的代碼,如下

    以下爲示例代碼

    @echo off
    
    setlocal enabledelayedexpansion
    for %%d in (A: B: C: D: E: F: G: H:) do (
     dir "%%d\sources\install.swm" /ad/b 2> nul 1> nul
     if !errorlevel! neq 0 (
      dir "%%d\sources\install.swm" /a-d/b 2> nul 1> nul
      if !errorlevel! equ 0 (echo %%d)
     )
    )
    endlocal
    

    以上爲示例代碼

    也可以基於卷標進行分析,以下爲使用 diskpart 的一種思路

    通過 find 或 findstr 分析

    echo list volume | diskpart

    的結果。

    echo list volume | diskpart

    參考至

    How to get a list of drive letters on a system through a windows shell (bat, cmd)?
    http://serverfault.com/questions/62578/how-to-get-a-list-of-drive-letters-on-a-system-through-a-windows-shell-bat-cmd/455345#455345

    直達出處
    http://serverfault.com/a/455345

    我和該文章一些回復者一樣,以爲執行 list volume 需要用到 -s 參數,其實不需要,直接通過管道傳輸即可,那樣更簡潔。


    Folding@Home

    2014年3月19日 14:53