有一段时间没再ITECN上更新了,主要因为工作上要完成OCS的SOP文档,所以把主要精力都投入在上面了。
前昨两天接到一个任务,需要帮助一些没有加域的计算机加域,并迁移用户的相关设置。经过接近一天时间的努力,用脚本配合moveuser,顺利完成了工作组下的用户平滑迁移到域用户,收获颇多,在此和诸位分享。也许在编写脚本的过程中,某些地方还有欠缺,还请各位多多包含。
先来描述下问题情境:
1. 用户使用本地计算机Users组成员帐号(假定用户名为user)登录计算机,并且使用了一段时间,用户配置文件偏大。且本地管理员密码不统一,可选的密码有三个。已分配IP地址。
2. 现在计划将这些用户批量加域,计算机需要重命名,并迁移用户的相关设置。
3. 整个过程对用户产生的影响越小越好,Helpdesk要进行的操作越少越好。
4. 所有脚本最好整合成WinRAR自解压包。
在描述具体实现过程前,诸位可以下载以下视频,查看最终效果。
从 SkyDrive 下载
下面来说说具体解决步骤。
首先第一个问题是,用户是以本地计算机Users组成员身份登录计算机的,在这种情况下,我们是没有办法使用他的用户来加域的,需要使用本地管理员组里的成员,如Administrator才行。但是这又引出另一个问题,管理员帐号密码不统一,我们需要使用一种方法使得脚本能遍历这三种可能性,最终用正确的密码以管理员身份运行重命名计算机的脚本。如果三个密码都无法使用,那只能给个提示劳驾Helpdesk的兄弟破解密码了。
那么如何实现以上需求呢?VBScript当然无法实现,他没有提供内置的方法来让我们以其它用户身份运行程序或脚本,而且遍历管理员密码也很难实现。那么我们就只能面对出师未捷身先死的窘境了么?当然不是,这个世界如此丰富多彩,早已有高人给我们提供了其它工具来实现这些需求,下面请出的是AutoIT,先来看看最终代码:
1: dim $pwd[3]
2: $pwd[0] = "pass@w0rd1"
3: $pwd[1] = "pass@w0rd2"
4: $pwd[2] = "pass@w0rd3"
5:
6: For $i = 0 to 2
7: $retVal = RunAs("Administrator",@ComputerName,$pwd[$i],1,"wscript.exe " & Chr(34) & @ScriptDir & Chr(34) & "\renamecomputer.vbs")
8: If $retVal = 0 Then
9: if $i+1 = 3 Then
10: MsgBox (0+16,"错误","管理员密码错误,请重设本地管理员密码。")
11: ExitLoop
12: EndIf
13: Else
14: ExitLoop
15: EndIf
16: Next
有关AutoIT语法的相关内容,我在这里就不在阐释了,有兴趣的朋友可以去找找相关资料。在以上代码中,我们首先声明一个数组来存放可能的管理员密码。接着我们使用For循环来遍历可能的管理员密码,如果三个已知密码都是错误的,那么我们会给Helpdesk以下提示:
如果三个密码中有一个正确,就会启动重命名计算机脚本(与AutoIT生成的程序在同一路径下,所以使用@ScriptDir常量,并且为了避免路径中的空格引发错误,所以使用了Chr(34)。而路径中有空格是因为将涉及到的文件都解压到%temp%目录下)
下面来说说重命名计算机的脚本。
1: Set objShell = CreateObject("Wscript.shell")
2: Set objFSO = CreateObject("Scripting.FileSystemObject")
3:
4: strComputerName = InputBox("请输入新计算机名","输入")
5: strLocalUserName = InputBox("请正确输入当前用户名" & vbcrlf & "否则将影响到用户配置文件迁移!","输入")
6: strDomainUserName = InputBox("请正确输入域账号,姓名的拼音,比如lisi" & vbcrlf & "否则将影响到用户配置文件迁移!","输入")
7: strScriptpath = Replace(WScript.ScriptFullName,WScript.ScriptName,"")
8:
9: Set objTXT = objFSO.CreateTextFile(strScriptpath & "\moveuser.bat")
10: objTXT.WriteLine("moveuser " & strLocalUserName & " contoso\" & strDomainUserName & " /k")
11: objTXT.Close
12:
13: objShell.Run("cmd /c net user administrator abcd@123")
14:
15: WScript.Sleep 1500
16:
17: strComputer = "."
18: Set objWMIService = GetObject("winmgmts:" _
19: & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
20:
21: Set colComputers = objWMIService.ExecQuery _
22: ("Select * from Win32_ComputerSystem")
23:
24: For Each objComputer in colComputers
25: strErr = objComputer.Rename(strComputername)
26: Next
27:
28: If strErr = 0 Then
29: objFSO.CopyFile strScriptpath & "\joindomain.vbs","c:\windows\system32\"
30: objFSO.CopyFile strScriptpath & "\moveuser.bat","c:\windows\system32\"
31: objFSO.CopyFile strScriptpath & "\moveuser.exe","c:\windows\system32\"
32: objshell.RegWrite "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce\joindomain", "c:\windows\system32\joindomain.vbs","REG_SZ"
33: MsgBox "单击确定重新启动计算机",vbOKOnly+vbInformation,"信息"
34: objshell.Run("cmd /c shutdown.exe -r -t 0")
35: End If
首先我们需要Helpdesk输入新计算机名,当前用户的登录名,将要使用的域账号名,这也是整个迁移过程中唯一需要用户输入的地方。当然这里使用简单的输入框会存在一点问题,如果单击输入框中的取消按钮,可能会导致整个加域任务失败。不过这个任务是由Helpdesk完成而不是普通用户,所以我们暂时不处理这个问题。接下来,我们取得当前脚本的运行路径,并在该路径下根据Helpdesk的输入信息来生成moveuser.bat供后续使用。接着我们需要做的是重设本地管理员的密码,当下一次重启时,Helpdesk需要使用管理员帐号登录,加域脚本会自动执行加域脚本和 moveuser.bat。
接下来我们利用脚本中心提供的示例代码重命名计算机,并将加域脚本,包含moveuser命令的批处理及moveuser命令本身复制到 system32目录下。同时我们向注册表里写入相关信息,使得下次登录时加域脚本能自动运行。最后脚本执行完成,然后单击确定后重新启动计算机。
重启完成后,Helpdesk将使用管理员帐号登录计算机,计算机会自动执行加域脚本joindomain.vbs。
加域脚本中的代码也不太复杂,下面先贴上代码:
1: Const JOIN_DOMAIN = 1
2: Const ACCT_CREATE = 2
3:
4: strdnsOne = "192.168.100.1"'InputBox("Please type your first DNS address","Input")
5: strdnsTwo = "192.168.100.2"'InputBox("Please type your second DNS address","Input")
6: strDomain = "contoso.com"'InputBox("Please type your domain name","Input")
7: strUser = "administrator"'InputBox("Please type your domain user name","Input")
8: strPassword = "abcd@123"'InputBox("Please type you domain user's password","Input")
9:
10: strComputer = "."
11:
12: Set objShell = CreateObject("Wscript.shell")
13: Set objFSO = CreateObject("Scripting.FileSystemObject")
14:
15: Set objWMIService = GetObject("winmgmts:" _
16: & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
17:
18: Set colNetCards = objWMIService.ExecQuery _
19: ("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = True")
20:
21: For Each objNetCard in colNetCards
22: arrDNSServers = Array(strdnsone, strdnstwo)
23: objNetCard.SetDNSServerSearchOrder(arrDNSServers)
24: Next
25:
26: Set objNet = CreateObject("Wscript.network")
27: strComputerName = objnet.ComputerName
28:
29: Set objComputer = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & _
30: strComputer & "\root\cimv2:Win32_ComputerSystem.Name='" & _
31: strComputername & "'")
32:
33: ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain, _
34: strPassword, strDomain & "\" & strUser, NULL, _
35: JOIN_DOMAIN + ACCT_CREATE)
36:
37: If ReturnValue = 0 Then
38: Set objCMD = objshell.Exec("c:\windows\system32\moveuser.bat")
39: Do While objCMD.Status = 0
40: WScript.Sleep 500
41: Loop
42: MsgBox "单击确定重新启动计算机",vbOKOnly+vbInformation,"信息"
43: objshell.Run("cmd /c shutdown.exe -r -t 0")
44: End If
首先声明加域脚本要用到的常量,接着我们将加域所用到的dns服务器地址,域名,加域用的帐号名和密码(实验环境中直接用的域管理员帐号,实际环境中我们当然不能用,可以使用一个专门的帐号来代替)存放在变量中。下面使用的代码来自脚本中心,我们只需根据具体环境相应修改即可。当加域成功后,脚本会运行之前创建的moveuser批处理来迁移用户配置文件,当迁移完成后,需要再次重启计算机。至此整个任务已经完成,接下来需要做的就是等待计算机重启完成后,使用域账号登录,验证是否达到了我们预想的效果。
总结几点:
1. 一开始的输入是关键,在使用这整套脚本之前,我们需要和Helpdesk强调这一点
2. 我们需要保证软硬件环境能使得加域成功,现有脚本中没有做任何处理
3. 用WinRAR生成自解压包时要注意解压路径的选择(start.exe即是用AutoIT写得脚本)
4. 关于虚拟机环境。诸位注意,我用的是Windows Server 2003做的实验,但是如果你在Windows Server 2003中创建普通帐号,这个账号是没有办法是没有重新启动计算机的,所以记得在本地安全策略里修改关闭系统的权限。
最后还是强调一句:
文中所有代码仅供参考,如需测试请在虚拟机中执行。本文的目的旨在提供思路而非最终解决方案。
已发表 2008年11月8日 14:59 作者 ghjconan
归档在:工作点滴
评论
# re: 一次moveuser的使用经历
啊,没看出门道啊。
我加域的时候用moveuser老失败。
我有俩问题:
1、加域和重命名计算机名是不能同时进行的,要重新启动。
2、moveuser需要指定old用户和new用户。哪么你的这个过程中是不是还有很多输入啊?
2008年11月8日 16:11 by smileruner
# re: 一次moveuser的使用经历
1. 所以我测试是重启了两次,第一次因为重命名计算机,第二次则是加域
2. 新老用户名在一开始就完成输入,并将命令保存在moveuser.bat中
2008年11月8日 16:22 by ghjconan
# re: 一次moveuser的使用经历
加域和重命名可以同步进行。
Moveuser可以通过变量读取现有用户名,要求输入新用户名,并通过规范的用户名来规范计算机名称,同时使用这个用户账号将计算机加入域。
2008年11月8日 16:34 by Lee
# re: 一次moveuser的使用经历
之所以要输入当前登录用户名是因为脚本是以管理员身份启动的,如果我试图想要通过环境变量获取当前登录用户名,得到的结果将会是是管理员的用户名……
至于计算机命名规则,真实环境中是根据资产编号定的……所以需要Helpdesk手动输入……
2008年11月8日 17:00 by ghjconan
# re: 一次moveuser的使用经历
re Lee
据我所知,如果先该计算机名,将无法加域。如果先加域,再改计算机名,将再次要求输入加域用户名和密码。
2008年11月9日 19:06 by smileruner
# re: 一次moveuser的使用经历
据我所知,我们项目组的同事写的脚本是可以这样做的。不过~~怎么做的我不知道。
2008年11月10日 15:42 by Lee
# re: 一次moveuser的使用经历
这篇文章写的不错,赞一个!
2008年11月10日 16:37 by ahpeng
# re: 一次moveuser的使用经历
先改机器名再加域是可以的,我已经测试过很多次了。
ghjconan的加域思路和我很相似,我已经把加域过程VB化了,呵呵。
2008年11月13日 15:08 by lzlutao
# re: 一次moveuser的使用经历
额,Lzlutao,先改计算机名再加域是不可取的。这样会导致你的计算机名更改失败。
2008年11月14日 15:24 by smileruner
# 从Ghjconan的《一次moveuser的使用经历》谈开。。。
Ghjconan在他的博客发表了文章《一次moveuser的使用经历》,原帖地址: http://blogs.itecn.net/blogs/ghjconan/archive/2008/11/08/moveuser.aspx