AutoDomainJoinForWorkgroup.wsf
WORKGROUPでクライアントPCを利用している環境で、ActiveDirectoryを導入した際、 何よりもコストがかかるのがクライアントPCのドメイン参加及びデータ移行作業です。
本スクリプトでは、以下のタスクに分類しリソースキットを利用しつつ全作業を自動化します。
実際、このスクリプトを利用した展開は延べ4000台程度ですが、
クライアントPCの不良や、ネットワークの不良、はたまた原因不明等で色々ありますが
スクリプト使用でのドメイン参加及びデータ移行の成功率は90%といったトコロです。
それでも人海戦術で行うよりは遥かに低コストで導入出来ます。
それでも人海戦術で行うよりは遥かに低コストで導入出来ます。
目的
ユーザーはスクリプトを実行し、自分のユーザー名を入力するだけで、 ドメイン参加からデータ移行まで、全てを自動化する概要
クライアントPCの環境をチェックし、ドメインへの参加を行い、 スクリプトを実行しているログオンユーザーのプロファイルを、 MoveUser.exeを利用して利用するドメインユーザーへ、プロファイルのコピーを実施し、 ドメインユーザーに対してローカルのAdministratorsの権限を付与します。動作フローチャート
動作タスク
- 移行先のドメインアカウントを入力
- クライアントPCの環境を確認
- オペレーティングシステムがスクリプトサポート範囲である事を確認
- スクリプトを実行しているアカウントに管理者権限があるかどうかを確認
- ネットワークの設定を確認
- 情報をレジストリに格納
- DNSサーバへ疎通を確認
- ドメイン参加
- ドメインアカウントをローカルの管理者へ追加
- 移行前の準備
- 移行
@.オペレーティングシステムを確認
A.ホスト名を確認
B.ローカルログオンユーザーを確認
C.移行先ドメインユーザーをを確認
@.DHCPかどうかを確認
A.DHCPの場合、DNSの設定値がDCのレコードを持っているDNSに向いているかを確認
B.固定IPの場合は、DNSの設定値を変更
@."HKLM\Software\ドメイン名\setupStartTime"にプログラムの開始時間
A."HKLM\Software\ドメイン名\oldLogonUserName"に旧ユーザー名
B."HKLM\Software\ドメイン名\oldLogonUserName"に新ユーザー名
@.DNSの設定を行う(動的更新、親サフィックスを明示して利用する様に設定)
A.ドメイン参加
@.入力したドメインアカウントが存在するかを確認
A.プロファイル移行用のアカウントにローカル管理者権限を追加
B.ドメインアカウントにローカル管理者権限を追加
@.スタートアップへ移行用アカウントでログオンした際にMoveuserが実行されるバッチファイルを追加
A.移行に利用するMoveuser.exeをC:\にコピー(コピー元はNetlogon共有)
B.移行に利用するスクリプトをC:\にコピー
C.移行用アカウントを自動ログオンに設定
D.再起動
@.Moveuserの実行(c:\moveuser.exe /y /k "コンピュータ名\旧ユーザー名 "ドメイン名\新ユーザー名") 移行の為の情報はレジストリから取得
A.実行結果を確認
B.次回ログオンのユーザー名を「新ユーザー名」に、ログオン先を「ドメイン名」に変更
C.ログオフ
スクリプトの説明
まず、使用する変数を定義します。// // スクリプト実行に必要なオブジェクトの作成 // var oNet = WScript.CreateObject("WScript.Network"); var oSh = WScript.CreateObject("WScript.Shell"); var oFs = WScript.CreateObject("Scripting.FileSystemObject"); var oShApp = WScript.CreateObject("Shell.Application"); // // WMIには再起動・ログオフを使用する為に"shutdown"を追加する // var oWmi = GetObject("winmgmts:{(Shutdown)}\\\\.\\root\\cimv2"); var oNetCon = oWmi.ExecQuery("select * from win32_NetworkAdapterConnection"); var oOs = oWmi.ExecQuery("select * from win32_OperatingSystem"); var OS_WindowsXP = "Microsoft Windows XP Professional"; var OS_Windows2000 = "Microsoft Windows 2000 Professional"; // // 実行中の内容をIEで表示させる // var oIe = WScript.CreateObject("InternetExplorer.Application"); oShApp.MinimizeAll(); oIe.Navigate("about:blank"); oIe.document.body.runtimeStyle.overflowX = "hidden" oIe.document.body.runtimeStyle.overflowY = "hidden" oIe.document.title = "ActiveDirectoryのセットアップ"; oIe.document.bgColor = "#115577"; oIe.StatusBar = false; oIe.MenuBar = false; oIe.ToolBar = false; oIe.Width = 600; oIe.Height = 800; oIe.Visible = true; oIe.FullScreen = true; // // 固有の情報を格納 // 使用する場合は以下の部分に対して修正が必要 // var DNSPRIMARY = "192.168.1.100"; //ADのゾーンを格納しているDNSサーバを指定 var MVUSRDIRSV = "\\\\192.168.1.100" //Moveuser.exeを保存している一番近いDCを指定(Netlogon共有に保存) var DOMAINNAME = "example"; //NetBIOSドメイン名を指定 var WAITTIME = 1500; //各処理の待ち時間(短くすると失敗するケースがある) // // 旧情報はNetworkオブジェクトから、新ユーザー名はプロンプトから入力 // var HOSTNAME = oNet.ComputerName; var OLDUSERNAME = oNet.UserName var NEWUSERNAME = checkCancel(inputUserName(),oIe);showIEMessage関数を使用して、画面上に指定した文字列を表示させます。
// // システム環境を事前に調査・表示します。 // showIEMessage(oIe,"システムの事前調査を行います"); showIEMessage(oIe," オペレーティングシステム:"+getClientOSVersion()); showIEMessage(oIe," コンピュータ名:"+HOSTNAME); showIEMessage(oIe," 旧ユーザー名:"+OLDUSERNAME); showIEMessage(oIe," 新ユーザー名:"+NEWUSERNAME); // // 最初に指定した秒数(ms)待機します。 // WScript.Sleep(WAITTIME);getClientOSStat関数を使用して、実行可能OSかどうかを判定します。
// // 実行環境のオペレーティングシステムを調査します。 // Windows XP以前の場合は実行を許可しません。 // showIEMessage(oIe,"処理を続行します。"); if( getClientOSStat() == false ) { showErrorMessage(1); oIe.Quit(); WScript.Quit(); } WScript.Sleep(WAITTIME);checkAdminMember関数を使用して、ログオンしているユーザーに管理者権限があるかを確認します。
// // ログオンアカウントがAdministratorsに属しているかを確認 // 権限が無い場合には実行を許可しません。 // showIEMessage(oIe,"ユーザー権限を確認しています。"); var returncode = checkAdminMember(OLDUSERNAME); if( returncode == false ) { showIEMessage(oIe," このユーザーには管理者権限がありません。"); showErrorMessage(2); oIe.Quit(); WScript.Quit(); } showIEMessage(oIe," このコンピュータに対する管理者権限を確認しました。"); WScript.Sleep(WAITTIME);checkNetworkPram関数を使用してネットワーク設定を確認・表示します。
ここでは、指定されたDNSサーバへリゾルバが設定されていない場合は以下の動作をします。
固定IP→DNSの設定値を指定された値にsetDnsSearchOrder関数を利用して強制
DHCP→エラー画面で停止(DHCPサーバ側でDNSを設定して下さい)
// // DHCP接続かどうかを確認します。 // また、DNSが指定されたアドレスである事を確認します。 // 固定IPでかつDNSが正常でない場合には、 // ここでDNS設定値を強制します。 // returnDhcpStat = checkNetworkPram("DHCPSTAT"); returnAddr = checkNetworkPram("DNSP"); showIEMessage(oIe,"ネットワーク接続を確認します。"); showIEMessage(oIe," DHCP状態 :"+returnDhcpStat); showIEMessage(oIe," IPアドレス :"+checkNetworkPram("IP")); showIEMessage(oIe," サブネット :"+checkNetworkPram("MASK")); showIEMessage(oIe," ゲートウェイ:"+checkNetworkPram("GW")); showIEMessage(oIe," DNSサーバー :"+returnAddr); if( returnDhcpStat == true ) { if( returnAddr != DNSPRIMARY) { showErrorMessage(4); oIe.Quit(); WScript.Quit(); } } else if( returnDhcpStat == false ) { if( returnAddr != DNSPRIMARY) { // // setDnsSearchOrder関数を利用してDNS設定値を投入 // showIEMessage(oIe,"DNSサーバーの設定を「"+DNSPRIMARY+"」へ更新しています。"); setDnsSearchOrder(DNSPRIMARY); } } WScript.Sleep(WAITTIME);Moveuser実行時に使用する値をレジストリに格納します。
// // レジストリの構成 // showIEMessage(oIe,"レジストリを構成します。"); showIEMessage(oIe," レジストリを構成します。"); var date = getSetupTime(); var regStartTime = "HKLM\\Software\\"+DOMAINNAME+"\\setupStartTime"; showIEMessage(oIe," "+regStartTime+":"+getNowDate()); oSh.RegWrite(regStartTime,date,"REG_SZ"); var regOldUserName = "HKLM\\Software\\"+DOMAINNAME+"\\oldLogonUserName"; showIEMessage(oIe," "+regOldUserName+":"+OLDUSERNAME); oSh.RegWrite(regOldUserName,OLDUSERNAME,"REG_SZ"); var regNewUserName = "HKLM\\Software\\"+DOMAINNAME+"\\newLogonUserName"; showIEMessage(oIe," "+regNewUserName+":"+NEWUSERNAME); oSh.RegWrite(regNewUserName,NEWUSERNAME,"REG_SZ"); WScript.Sleep(WAITTIME);checkPingStatus関数を利用して、DNSサーバへのPING疎通確認を行います。
// // DNSサーバーへのPING疎通確認 // showIEMessage(oIe,"DNSサーバーへのネットワーク疎通確認を実施します。"); var count = 0; for ( i = 0 ; i < 5 ; i++ ) { var pingStat = checkPingStatus(DNSPRIMARY); if( pingStat == 0 ) { showIEMessage(oIe," "+DNSPRIMARY+":DNSサーバーへの疎通を確認しました"); break; } else if( pingStat == 11010 ) { showIEMessage(oIe," "+DNSPRIMARY+":Request Timed Out"); } else { showIEMessage(oIe," "+DNSPRIMARY+":不明なエラー:"+pingStat); } if( i == 4 ) { showErrorMessage(3) oIe.Quit(); WScript.Quit(); } }clientDomainSetup関数を利用してドメイン参加を実施します。
ドメイン参加に失敗した場合はDNSの設定を再設定し、WORKGROUPへ降格して 再度ドメイン参加を実施します。10回試行しても正常に参加出来ない場合は、 エラーになります。
// // ドメイン参加 // showIEMessage(oIe,"ActiveDirectoryドメインへセットアップします。"); for( var i=1 ; i <= 10 ; i++ ) { returnCode = clientDomainSetup(3); showIEMessage(oIe," ドメイン参加の"+i+"回目の試行・・・"+returnCode); if( returnCode == 0 ) { showIEMessage(oIe,"ドメイン参加が成功しました。"); break; } WScript.Sleep(WAITTIME); if( i == 10 ) { showErrorMessage(4) oIe.Quit(); WScript.Quit(); } // // キャッシュのクリア、DNSの設定を実施 // setDnsParam(); setDnsSearchOrder(DNSPRIMARY); WScript.Sleep(WAITTIME); // // WORKGROUPへ降格 // clientDomainSetup(0); WScript.Sleep(WAITTIME); }checkDomainUserStatを利用して、新ユーザーが指定されたドメインに存在するかを確認します。
// // 新ドメインアカウントの存在確認 // showIEMessage(oIe,"netコマンドを利用して、ローカルAdministratorsにプロファイル移行用のアカウント(ikou)と、 新ユーザーに対してメンバーに追加を行います。
ドメインアカウントの存在確認を実施します。"); var userStat = checkDomainUserStat(NEWUSERNAME); if( userStat == false ) { showErrorMessage(5) oIe.Quit(); WScript.Quit(); } showIEMessage(oIe," 存在を確認しました。");
// // ユーザーへの権限追加 // ドメイン上のプロファイル移行用のアカウント(ikou)及び、NEWUSERNAMEの2アカウントへ権限を付与する // showIEMessage(oIe,"プロファイル移行用アカウントへの権限を付与します。"); var ikouUserName = "example\\ikou"; for( var i=1 ; i <= 10 ; i++ ) { oSh.run("cmd /c net localgroup administrators /add "+ikouUserName,0,true); returnCode = checkAdminMember(ikouUserName); if( returncode == true ) { showIEMessage(oIe,ikouUserName+"への権限追加を確認しました。"); break; } setAdminMember(ikouUserName); if( i == 10 ) { showErrorMessage(6) oIe.Quit(); WScript.Quit(); } } showIEMessage(oIe,"新アカウントへの権限を付与します。"); for( var i=1 ; i <= 10 ; i++ ) { oSh.run("cmd /c net localgroup administrators /add "+NEWUSERNAME,0,true); returnCode = checkAdminMember(NEWUSERNAME); if( returncode == true ) { showIEMessage(oIe,NEWUSERNAME+"への権限追加を確認しました。"); break; } setAdminMember(NEWUSERNAME); if( i == 10 ) { showErrorMessage(7) oIe.Quit(); WScript.Quit(); } }Allusersのスタートアップにバッチファイルを作成します。
こうする事で、プロファイル移行用アカウントでログオンした際に、 自動的にMoveuser.exeを実行する事が可能になります。
バッチファイルで実行されるスクリプト及びMoveuser.exeはC:\にコピーされます。
// // プログラムをスタートアップへ登録します。 // showIEMessage(oIe,"
プログラムをAll Usersスタートアップへ登録します。"); for( var i=1 ; i <= 10 ; i++ ) { try { var allUsersProfile = oSh.ExpandEnvironmentStrings("%allusersprofile%"); var secondJobBatFile = allUsersProfile+"\\スタート メニュー\\プログラム\\スタートアップ\\setup(second).bat"; var fsSecondJobStart = oFs.OpenTextFile(secondJobBatFile,2,true,0); fsSecondJobStart.WriteLine("@echo off"); fsSecondJobStart.WriteLine("cscript c:\\setup.wsf //Job:SecondStep"); fsSecondJobStart.WriteBlankLines(1); fsSecondJobStart.Close(); if( oFs.FileExists( secondJobBatFile ) ) { showIEMessage(oIe,"スタートアッププログラムの登録を確認しました。"); break; } if( i == 10 ) { showErrorMessage(8) oSh.run("cmd /c del "+secondJobBatFile,0,false); oIe.Quit(); WScript.Quit(); } } catch(e) { oSh.popup("スタートアップ登録中に予期しないエラーが発生しました。 \nもう一度実行するか管理者へ問い合わせて下さい。\n\n" + "エラーの名前:"+ e.name + "\nメッセージ:"+ e.message + "\nエラー番号:"+ Number(e.number & 0xffff).toString() + "\nエラーの説明:" + e.description,600,"例外処理:処理は停止します。",16); oSh.run("cmd /c del "+secondJobBatFile,0,false); oIe.Quit(); WScript.Quit(); } }