URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。
Windowsを管理してゆく上でのTipsやスクリプトなどを紹介します。
あるフォルダを別なフォルダにミラーリングコピーする方法としては,以前はxcopyを使うことが多かったが,この方法だと転送元から削除されてい るのに転送先に残っている不要なファイルの削除はできません。 僕自身はそういうゴミを削除するコマンドを別に作って運用していましたが,Windows Resource Tool KitにRobocopyというコマンドがあるのを見つけました(Winodws VISTAには最初からRobocopyが入っています)。これは,そのような不要ファイルを削除することもできるすぐれもので,Linuxのrsync のように便利なツールです。このRobocopyについて色々と調べてみました。
Resurce Kitをインストールするとrobocopy.exeと同じフォルダにrobocopy.docというドキュメントもインストールされます。ここには Robocopyを使う上での有益な情報が詰まっているのですが,いかんせん英文です。なんとかがんばってこれを日本語訳してみました。それがRobocopy.docの日本語版 です。ここでは,その作業の中で得られたものから特に便利そうなものを抜粋して掲載します。Robocopyに関する日本語サイトで最も詳しいページを目指します。
usageは次の通りです。
ROBOCOPY source destination [file [file]...] [options]
引数 |
意味 |
コメント |
|
||
source |
転送元ディレクトリ |
drive:\path や \\server\share\pathYが使用できます。 |
destination |
転送先ディレクトリ |
drive:\path や \\server\share\pathYが使用できます。 |
file |
処理対象となるファイル名 |
ワイルドカード文字 (? and *)が使えます。指定されたなかった場合はデフォルトで全てのファイル (*.*)となります。 |
options |
コマンドラインオプション |
利用できるオプションは、後述します。 |
転送元から転送先にファイルをコピーします。fileを指定することで転送元のファイルから特定のものだけをコピーするように指定することもできます。
robocopy C:\userdata \\server\userdata /MIR
上記の例では,C:\userdataの内容を \\serverというサーバーにあるuserdataフォルダにミラーリングコピーします。/MIRはミラーリングするときに使うオプションで,サブ フォルダもコピーすることと,転送元にない不要ファイルを転送先から削除することを指定します。
オプションスイッチの意味は次の通り。これは
robocopy /?
で表示されるヘルプを日本語化したもの。(VISTAのrobocopyはこのヘルプも日本語化されていますが,それとは若干表現が違うと思います。)
オプションスイッチの実際の利用例はこちらをご覧ください。
/S :: 空でないサブフォルダをコピーする /E :: 空のものも含めてサブフォルダをコピーする /LEV:n :: ソースフォルダツリーの中の上位nレベルのみをコピーする /Z :: 再開可能(restartable)モードでファイルをコピーする /B :: バックアップモードでファイルをコピーする /ZB :: 可能なら再開可能モードで不可ならバックアップモードを使う /COPY:copyflag[s] :: copyflagで指定されたファイル情報をコピーする(デフォルトは /COPY:DAT) D : データ A : 属性 T : タイムスタンプ S : セキュリティ情報(NTFSのACL) O=所有者情報 U=ファイル監査情報 /SEC :: セキュリティもコピーする (/COPY:DATSと同じ) /COPYALL :: すべてのファイル情報をコピーする(/COPY:DATSOUと同じ) /NOCOPY :: ファイル情報をコピーしない (/PURGEと一緒に使える) /PURGE :: コピー元にないファイル・フォルダを削除する /MIR :: ミラーリングコピーする (/Eと/PURGEを指定したのと同じ) /MOV :: ファイルを移動する (コピー後にコピー元から削除する) /MOVE :: ファイルとフォルダを移動する(コピー後にコピー元から削除する) /A+:[RASHNT] :: コピーされたファイルに与えられた属性を加える /A-:[RASHNT] :: コピーされたファイルから与えられた属性を削除する R : Read only S : System N : Not content indexed A : Archive H : Hidden T : Temporary /CREATE :: フォルダツリーと0バイトのファイルを作るのみ (データはコピーされない) /FAT :: コピー先ファイルを8.3形式のファイル名で作成する /FFT :: FATタイムスタンプ(精度2秒)であると見なす /256 :: 非常に長いパス(256文字以上)のサポートをオフにする /MON:n :: コピー元を監視,n回以上の変更があったら再実行する /MOT:m :: コピー元を監視,m分後に変更があったら再実行する /RH:hhmm-hhmm :: コピー開始できる時間帯を指定します /PF :: 処理単位ではなくファイル単位で実行時間帯のチェックをする /IPG:n :: 遅い回線の帯域を確保するためパケット間にnミリ秒のギャップを挿入する
/A :: Archiveビットがセットされているファイルのみをコピーする /M :: Archiveビットがセットされているファイルのみをコピーし ビットをリセットします。 /IA:[RASHCNETO] :: 指定された属性がセットされているファイルのみを含む /XA:[RASHCNETO] :: 指定された属性がセットされているファイルを除外する /XF file [file]... :: 指定されたファイル名(パス,ワイルドカード)と一致するファイルを除外する /XD dirs [dirs]... :: 指定されたフォルダ名(パス)と一致するフォルダを除外する 次のファイルの種類についてはファイルクラスを参照。 /XC :: Changedファイルを除外する /XN :: Newerファイルを除外する /XO :: Olderファイルを除外する /XX :: Extraファイルとフォルダを除外する /XL :: Lonelyファイルとフォルダを除外する /IS :: Sameファイルを含む /IT :: Tweakedファイルを含む /MAX:n :: nバイトより大きいファイルを除外する /MIN:n :: nバイトより小さいファイルを除外する /MAXAGE:n :: 指定日よりも古いファイルを除外する /MINAGE:n :: 指定日よりも新しいファイルを除外する /MAXLAD:n :: 指定日以降に使われていないファイルを除外する /MINLAD:n :: 指定日以降に使われているファイルを除外する nの指定は,nが1900以下の時はnは現在からさかのぼる日数。nがYYYYMMDD形式ならその日付 /XJ :: 境界値を含まない (デフォルトでは境界値を含む)
/R:n :: コピーに失敗したときにリトライする回数。デフォルトは1000000 /W:n :: リトライの間にウェイとする秒数。デフォルトは30秒 /REG :: /R:n と /W:n オプションをレジストリに初期値として保存する /TBD :: wait for sharenames To Be Defined (retry error 67).
/L :: ファイルのリストを表示するのみ。ファイルのコピーなどはしない /X :: report all eXtra files, not just those selected. /V :: produce Verbose output, showing skipped files. /TS :: ソースファイルのタイムスタンプを出力に含む /FP :: ファイルのフルパス名を出力に含む /NS :: ファイルサイズをログに出力しない /NC :: ファイルクラス(New File, Newer, Olderなど)をログに出力しない /NFL :: ファイル名をログに出力しない /NDL :: フォルダ名をログに出力しない /NP :: 進捗を表示しない(%コピーという表示をしない) ログファイルに記録するときはこれを指定した方がよい /ETA :: コピーに要する推定時間を表示する /LOG:file :: ログファイルに出力する(存在するファイルには上書き) /LOG+:file :: ログファイルに出力する(存在するファイルには追加) /TEE :: ログファイルと同じようにコンソールにも出力する /NJH :: ジョブヘッダを出力しない /NJS :: ジョブサマリを出力しない
/JOB:jobname :: jobnameで指定されたファイルからパラメータを得る /SAVE:jobname :: jobnameで指定されたファイルにパラメータを保存する /QUIT :: コマンドラインの解析をした後に終了する(パラメータを表示する) /JOBと一緒に使うとジョブファイルの内容を表示する /NOSD :: コピー元が指定されないと断言します。 コピー元が実行時で提供されるテンプレートジョブに役立つ。 /NODD :: コピー先が指定されないと断言します。 コピー先が実行時で提供されるテンプレートジョブに役立つ。 /IF :: 指定された名前のファイルをインクルードする。ジョブファイルでのみ使う
Robocopyでは,転送元と転送先のフォルダでのファイルの状態によって,対象ファイルを分類して処理します。次の表をご覧ください。
クラス | 説明 |
Lonely | 転送元には存在するが転送先に存在しないファイル |
Same | 転送元と転送先両方に存在し,タイムスタンプもサイズも属性も同じファイル |
Tweaked | 転送元と転送先両方に存在し,タイムスタンプもサイズも同じだが,属性だけが異なるファイル |
Changed | 転送元と転送先両方に存在し,タイムスタンプは同じだが,サイズ異なるファイル |
Newer | 転送元と転送先両方に存在するが,転送元のタイムスタンプの方が新しいファイル |
Older | 転送元と転送先両方に存在するが,転送元のタイムスタンプの方が古いファイル |
Extra | 転送元に存在しないのに転送先に存在するファイル |
Mismatched | 転送元のファイルと同名のディレクトリが転送先にある(あるいはその逆) |
デフォルトではChanged, Newer, Olderがコピーされます。ファイルクラス毎にコピーするかどうかを変更するのに/XC /XN /XO /XX /XL /IS /ITなどのオプションを使用します。Extraファイルを転送先フォルダから削除するには,/PURGEか/MIRオプションを指定します。
/MON:nと/MOT:mスイッチを使用して、転送元のディレクトリツリーの変化をモニタして,変化が起こるたびにファイルをコピーすることがで きます。/MON:nスイッチは,n回以上の変更があったらコピーを開始することを指定します。/MOT:mスイッチは前回のコピーからm分以上経過した らコピーを開始することを指定します。これらのスイッチのうちの1つだけ指定した場合は,もう一方には1の値を指定したことになります。これらが指定され ていたら,Robocopyは終了しないで常駐プログラムかサービスのように監視を続けます。こうしてRobocopyでモニタリングしていると,転送元 のファイルへの変更結果をほぼリアルタイムで転送先へも反映させることができます。
robocopy C:\userdata \\server\userdatta /MIR /MON:5 /MOT:5
このように指定するとC:\userdataに5回以上の変更が加えられて,前回のコピーから5分以上たっていたらコピー作業が始まります。
これらと一緒に/RHオプションが指定されると,実行可能な時間帯も設定できます。
robocopy C:\userdata \\server\userdatta /MIR /MON:5 /MOT:5 /PH:2200-0600
こんな風にするとコピーが開始する時間帯は夜の10時から朝の6時までになり,それ以外の時間帯はスリープしつづけます。このRHコマンドは作業の開始時間を指定するためにも使えます。
robocopy C:\userdata \\server\userdatta /MIR /PH:1900-2000
この場合だと19時まで待ってコピーを始め,コピーが終了したらrobocopyは終了します。
Robocopyの機能は豊富ですしコマンドラインオプションもたくさんあります。フルに機能を使おうとするとコマンドラインが長~くなってしまいます。そういうときにはジョブファイルというのが使えるようになっています。一度次のコマンドを実行してみてください。
robocopy /SAVE:test /QUIT
するとカレントディレクトリにtest.RCJというファイルができると思います。これがジョブファイルです。ジョブファイルはコマンドラインのオ プションを記録したものです。各行にオプションを指定します。二つのコロン以降はコメントになります。複雑なコマンドラインになるような処理はジョブファ イルを記録しておくと便利です。利用するときには次のようにします。
robocopy /JOB:test
さらに便利なのはジョブファイルは複数使用できるという点です。ただし転送元と転送先は1回しか指定できません。転送元や転送先を指定しないジョブファイルには,/NOSD /NODDオプションを記述しておきます。
添付 | サイズ |
---|---|
Robocopy.docの日本語版 | 367.5 KB |
Robocopy のオプションはめちゃくちゃたくさんあります。ここでは,実際に場面でのその組み合わせ例を示します。
robocopy C:\userdata \\server\userdata /MIR
ソースパスの内容ととデスティネーションパスを全く同じにしたい場合に使います。/MIR オプションは,/E(空のものも含めてサブフォルダをコピーする) と /PURGE(コピー元にないファイル・フォルダを削除する) を両方指定したのと等価です。
コピー先のファイル削除をしない場合は
robocopy C:\userdata \\server\userdata /E
のように /E のみを指定します。
エクスプローラなどで,フォルダごとコピーしようとすると,長い間待たされたあげくエラーが発生して途中で止まってしまいガッカリという経験はありませんか。「とりあえず,エラーが起きないファイルだけでも全部コピーしてくれればいいのに」
robocopy C:\userdata \\server\userdata /R:0 /E
/R:0で,エラー時のリトライ回数を0にしています。/Eオプションでソースフォルダの全てをコピーします。これでソースフォルダのファイルをエラーが発生しても中断することなくコピーできます。
robocopy C:\userdata \\server\userdata /R:0 /E /NFL /NP > COPY.LOG
さらに/NPや/NFLオプションをつけるて出力をファイルにリダイレクトすると,エラーが発生したログだけが残りますので,コピーできなかったファイルを特定できます。
デスティネーションフォルダにはあるがソースフォルダにないファイル。Robocopy のファイルクラスで言うとExtra。いわゆるゴミのファイルですが,そのファイルを削除するのみで,ファイルのコピーは行わないということもできます。
robocopy C:\userdata \\server\userdata /NOCOPY /PURGE
Robocopy のデフォルトの動作では,Changed, Newer, Older のクラスのファイルがコピーされます。ソースの方が古いファイルであっても,コピーされるのです。古いファイルはコピー対象から外したい場合は/XOオプションを使います。
robocopy C:\userdata \\server\userdata /MIR /XO
NAS製品の中では,タイムスタンプの精度がFATなみの2秒というものがあります。そのくせNTFSを名乗っている。そんなディスクとNTFSのディスクの間でミラーリングをした場合に,タイムスタンプの精度の違いにより,毎回コピーされてしまったり全然コピーされなかったりとうまく動かないことがあります。そんなことが起きないように,/FFT オプションを使います。
robocopy C:\userdata \\server\userdata /MIR /FFT
/FFTでは,タイムスタンプの精度が2秒であるとしてコピーをします。
コピーではなくフォルダ構造全体を移動したい場合は,/MOVE オプションを使います。
robocopy C:\userdata \\server\userdata /MOVE
よく似た感じの /MOV オプションもあります。こちらは,コピー元に(空っぽの)フォルダ構造が残ります。/MOVE ではフォルダも含めて元のパスにはなにもなくなります。
このトピックはExcelのVBAを利用してActive Directoryのユーザ情報を管理するやり方についての記録です。
Active DS Type Library Microsoft Scription Runtime Microsoft WMI cripting V1.2 Library
上記を追加する
後のCADSIを使うための構造体TADUserを定義している
Type TADUser UserID As String FirstName As String LastName As String Password As String DisplayName As String UserPrincipalName As String Description As String ScriptPath As String PasswordMustChange As Boolean Mail As String End Type
ADSIでユーザ管理を行うためのクラス
Option Explicit Const LDAP = "LDAP:" Const USER_NOT_FOUND = -2147016656 Private m_ServerBaseDN As String Private m_ServerAddress As String Private m_AdminName As String Private m_AdminPass As String Private Function GetContainer(APath As String) As IADsContainer Dim objDSO As IADsOpenDSObject Set objDSO = GetObject("LDAP:") Set GetContainer = objDSO.OpenDSObject( _ APath, m_AdminName, m_AdminPass, ADS_USE_ENCRYPTION And ADS_SECURE_AUTHENTICATION) End Function Private Function GetADUser(APath As String) As IADsUser Dim objDSO As IADsOpenDSObject Set objDSO = GetObject("LDAP:") Set GetADUser = objDSO.OpenDSObject( _ APath, m_AdminName, m_AdminPass, ADS_USE_ENCRYPTION And ADS_SECURE_AUTHENTICATION) End Function Private Function GetADGroup(APath As String) As IADsGroup Dim objDSO As IADsOpenDSObject Set objDSO = GetObject("LDAP:") Set GetADGroup = objDSO.OpenDSObject( _ APath, m_AdminName, m_AdminPass, ADS_USE_ENCRYPTION And ADS_SECURE_AUTHENTICATION) End Function Private Function GetLDAPPath(AObj As String) As String Dim result As String result = LDAP & "//" & m_ServerAddress & "/" If Len(AObj) > 0 Then result = result & AObj & "," End If result = result & m_ServerBaseDN GetLDAPPath = result End Function Private Sub SetUserProperty(AUser As IADsUser, AProperty As String, AValue As String, _ Optional isNew As Boolean = False) If Len(AValue) = 0 Then If Not isNew Then AUser.PutEx ADS_PROPERTY_CLEAR, AProperty, Null End If Else AUser.Put AProperty, AValue End If End Sub Public Sub OpenDomain(ServerAddress As String, ServerBaseDN As String, _ AdminName As String, ADMINPASS As String) m_ServerBaseDN = ServerBaseDN m_ServerAddress = ServerAddress m_AdminName = AdminName m_AdminPass = ADMINPASS End Sub Public Function QueryUser(AUserName As String, AContainerName As String) As Boolean Dim User As IADsUser On Error GoTo ERROR_QUERYUSER Set User = GetADUser(GetLDAPPath("cn=" & AUserName & "," & AContainerName)) QueryUser = True Exit Function ERROR_QUERYUSER: QueryUser = False End Function Public Function GetUser(AUserName As String, AContainerName As String) As TADUser Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath("cn=" & AUserName & "," & AContainerName) Set objUser = GetADUser(sPath) On Error Resume Next GetUser.UserID = objUser.Get("cn") GetUser.LastName = objUser.Get("sn") GetUser.FirstName = objUser.Get("givenName") GetUser.DisplayName = objUser.Get("displayName") GetUser.Description = objUser.Get("description") GetUser.PasswordMustChange = objUser.Get("pwdLastSet") = 0 GetUser.ScriptPath = objUser.Get("scriptPath") GetUser.UserPrincipalName = objUser.Get("userPrincipalName") GetUser.Mail = objUser.Get("mail") Err.Clear End Function Public Sub ChangePassword(AUserName As String, AContainerName As String, _ ANewPassword As String) Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath("cn=" & AUserName & "," & AContainerName) Set objUser = GetADUser(sPath) objUser.SetPassword ANewPassword objUser.SetInfo End Sub Public Sub ChangePassword2(AUserName As String, ANewPassword As String) Dim objUser As IADsUser Dim sPath As String Dim objDSO As IADsOpenDSObject sPath = "WinNT://" & m_ServerAddress & "/" & AUserName & ",User" Set objDSO = GetObject("WinNT:") Set objUser = objDSO.OpenDSObject( _ sPath, m_AdminName, m_AdminPass, ADS_USE_ENCRYPTION And ADS_SECURE_AUTHENTICATION) objUser.SetPassword ANewPassword objUser.SetInfo End Sub Public Sub SetUserEnable(AUserID As String, AContainerName As String, AEnable As Boolean) 'IDやパスワード以外のプロパティをセットする Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath("cn=" & AUserID & "," & AContainerName) Set objUser = GetADUser(sPath) objUser.AccountDisabled = Not AEnable objUser.SetInfo End Sub Public Sub UpdateUser(AUser As TADUser, AContainerName As String) 'IDやパスワード以外のプロパティをセットする Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath("cn=" & AUser.UserID & "," & AContainerName) Set objUser = GetADUser(sPath) SetUserProperty objUser, "sn", AUser.FirstName SetUserProperty objUser, "givenName", AUser.LastName SetUserProperty objUser, "displayName", AUser.DisplayName SetUserProperty objUser, "description", AUser.Description SetUserProperty objUser, "scriptPath", AUser.ScriptPath SetUserProperty objUser, "mail", AUser.Mail If AUser.PasswordMustChange Then objUser.Put "pwdLastSet", 0 Else objUser.Put "pwdLastSet", -1 End If objUser.SetInfo End Sub Public Sub SetHomeFolder(AUserID As String, AContainerName As String, _ ADrive As String, AHomePath As String) 'IDやパスワード以外のプロパティをセットする Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath("cn=" & AUserID & "," & AContainerName) Set objUser = GetADUser(sPath) SetUserProperty objUser, "homeDrive", ADrive SetUserProperty objUser, "homeDirectory", AHomePath objUser.SetInfo End Sub Public Sub AddUser(AUser As TADUser, AContainerName As String) Dim objCont As IADsContainer Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath(AContainerName) Set objCont = GetContainer(sPath) Set objUser = objCont.Create("User", "cn=" & AUser.UserID) SetUserProperty objUser, "sAMAccountName", AUser.UserID, True SetUserProperty objUser, "sn", AUser.LastName, True SetUserProperty objUser, "givenName", AUser.FirstName, True SetUserProperty objUser, "displayName", AUser.DisplayName, True SetUserProperty objUser, "description", AUser.Description, True SetUserProperty objUser, "scriptPath", AUser.ScriptPath, True SetUserProperty objUser, "userPrincipalName", AUser.UserPrincipalName, True SetUserProperty objUser, "mail", AUser.Mail, True objUser.SetInfo objUser.ChangePassword "", AUser.Password objUser.AccountDisabled = False objUser.Put "userAccountControl", ADS_UF_NORMAL_ACCOUNT Or ADS_UF_DONT_EXPIRE_PASSWD objUser.SetInfo If AUser.PasswordMustChange Then objUser.Put "pwdLastSet", 0 Else objUser.Put "pwdLastSet", -1 End If End Sub Public Sub DeleteUser(AUserName As String, AContainerName As String) Dim objCont As IADsContainer Dim objUser As IADsUser Dim sPath As String sPath = GetLDAPPath(AContainerName) Set objCont = GetContainer(sPath) objCont.Delete "User", "cn=" & AUserName End Sub Public Sub AddToGroup(AUserPath As String, AUserName As String, AGroupPath As String, _ AGroupName As String) Dim objGroup As IADsGroup Dim objUser As IADsUser Set objGroup = GetADGroup(GetLDAPPath("cn=" & AGroupName & "," & AGroupPath)) Set objUser = GetADUser(GetLDAPPath("cn=" & AUserName & "," & AUserPath)) objGroup.Add objUser.ADsPath End Sub Function QueryMember(AUserName As String, AGroupPath As String, AGroupName As String) Dim objGroup As IADsGroup Dim objUser As IADsUser Dim result As Boolean Set objGroup = GetADGroup(GetLDAPPath("cn=" & AGroupName & "," & AGroupPath)) result = False For Each objUser In objGroup.Members If objUser.Get("cn") = AUserName Then result = True Exit For End If Next QueryMember = result End Function Public Property Get ServerBaseDN() As String ServerBaseDN = m_ServerBaseDN End Property Public Property Let ServerBaseDN(ByVal ANewValue As String) m_ServerBaseDN = ANewValue End Property Public Property Get ServerAddress() As String ServerBaseDN = m_ServerBaseDN End Property Public Property Let ServerAddress(ByVal ANewValue As String) m_ServerBaseDN = ANewValue End Property
次回からこのCADSIクラスを使用してADのユーザ管理をするコードを書いてゆく。
Active Directoryのユーザ管理 (1) で作成したクラスを使って実際のユーザー管理をしてゆく。
ドメインユーザを追加するとともに,ホームディレクトリを作り,そのホームディレクトリをH:ドライブに割り当てるようにする。という仕様でやってみる。ドメインなどの仕様は次の通りとする。
ドメイン名 | hogedom.local |
ドメインコントローラ | domainserver |
ファイルサーバ | hogeserver |
home共有 | \\hogeserver\home以下に置く |
home共有のサーバ上の位置 | D:\home |
次のような感じのExcelの表があり,その表の中のデータを登録する手続を記述する。
部署 | 職員番号 | ID | 姓 | 名 | グループ | グループ | PW |
内野 | F008 | mac | 金子 | 誠 | fighters | naiya | abcd1234 |
外野 | F001 | hichori | 森本 | 稀哲 | fighters | gaiya | efgh5678 |
投手 | F021 | hisashi | 武田 | 久 | fighters | pitcher | vwxy9876 |
Sub cmdAddUsers_Click() Dim vID As String Dim iCol As Integer Dim iRow As Integer Dim ADSI As CADSI Dim User As TADUser Dim vGroup Set ADSI = New CADSI ADSI.OpenDomain _ "hogeserver.hogedom.local", "dc=hogedom,dc=local", "hogedom\administrator", "password" iRow = 2 vID = Cells(iRow, 3) Do While Len(vID) > 0 'Debug.Print vID User.UserID = vID User.LastName = Cells(iRow, 4) User.FirstName = Cells(iRow, 5) User.DisplayName = User.LastName + " " + User.FirstName User.Description = User.DisplayName User.PasswordMustChange = False User.ScriptPath = "logon.vbs" User.UserPrincipalName = vID & "@hogedom.local" User.Password = Cells(iRow, 8) ADSI.AddUser User, "ou=Persons" For iCol = 6 To 7 vGroup = Cells(iRow, iCol) If Len(vGroup) > 0 Then ADSI.AddToGroup "ou=Persons", vID, "ou=Groups", Cells(iRow, iCol) End If Next iCol ADSI.SetHomeFolder vID, "ou=Persons", "H:", "\\hogeserver\home\%username%" MakeFolder vID iRow = iRow + 1 vID = Cells(iRow, 3) Loop Set ADSI = Nothing End Sub
CADSIのSetHomeFolderメソッドを使って,ホームディレクトリを設定している。ドメインコントローラ上の「Active Directoryユーザとコンピュータ」にてユーザ設定をする際に,このコードのように //hogeserver/home/%username% のように指定すると自動的にフォルダが作成されるがADSIを使ってコードで設定した場合には自動作成されないようだ。
ADSIを利用したユーザー管理プログラムでアカウントを作成するのであれば,ホームフォルダの作成機能は必須となる。ここでは,//hogeserver/home という共有フォルダの下に各個人のホームディレクトリを作成し,そのアカウントに対してのみフルコントロールのアクセス権を付与する仕様を目指した。
上記のコード中で呼び出されているMakeFolder手続で,ホームディレクトリを作成している。
Sub MakeFolder(AID As String) Dim FSO As FileSystemObject Set FSO = New FileSystemObject FSO.CreateFolder "\\hogeserver\home\" & AID SetSecurityHomeDirectory "domainserver", "hogeserver", AID, "D:\home\" & AID Set FSO = Nothing End Sub
FileSystemObjectオブジェクトでフォルダをサーバ上に作成し,SetSecurityHomeDirectoryで権限を設定して いる。NTFSの権限を設定するにはコマンドラインではcaclsというツールがあったり,xcacls.vbsというツールをMicrosoftが提供 してくれていたりするが,なんとかExcelのVBA上でやりたかった。WMIを使って権限を設定するのは可能だが,とっても難しく面倒くさい。いくつか のサイトを覗いたが,このSetSecurityHomeDirectoryは,中の技術日誌ブログに あった同名の手続を改造して作った。用途がもろに僕のやりたいこと「ユーザのホームディレクトリの設定」と一致していたためだ。この手続では,指定した ユーザのフルコントロールのアクセス権をフォルダに与える。以下のコードはまんまパクリです。中さんのコードですのでそこのとこはよろしくです。
Function SetSecurityHomeDirectory(strDomainControler, strComputer, strUser, HomePath) Dim objWMIService Dim wmiAccounts Dim wmiAccount Dim wmiTrustee Dim wmiTrusteeClass Dim wmiSID Dim wmiACE Dim wmiACEClass Dim wmiFileSecSetting Dim wmiSecurityDescriptor Dim DictACE Dim obj Dim RetVal Dim i Set objWMIService = GetObject( _ "winmgmts:{impersonationLevel=impersonate}!\\" & strDomainControler & "\root\cimv2") SetSecurityHomeDirectory = False 'アカウント取得 Set wmiAccounts = _ objWMIService.ExecQuery("select * from Win32_Account where Name='" & strUser & "'") For Each obj In wmiAccounts Set wmiAccount = obj Exit For Next 'Trusteeに変換する Set wmiTrusteeClass = objWMIService.Get("Win32_Trustee") Set wmiTrustee = wmiTrusteeClass.spawnInstance_() Set wmiSID = objWMIService.Get("Win32_SID.SID='" & wmiAccount.sid & "'") wmiTrustee.DOMAIN = wmiSID.ReferencedDomainName wmiTrustee.Name = wmiSID.AccountName wmiTrustee.sid = wmiSID.BinaryRepresentation wmiTrustee.sidLength = wmiSID.sidLength wmiTrustee.sidString = wmiSID.sid 'ACEオブジェクトを作成する Set wmiACEClass = objWMIService.Get("Win32_ACE") Set wmiACE = wmiACEClass.spawnInstance_() wmiACE.AccessMask = 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 65536 _ + 131072 + 262144 + 524288 + 1048576 ' 2032127 wmiACE.Trustee = wmiTrustee wmiACE.AceType = 0 wmiACE.AceFlags = 3 '対象フォルダのセキュリティデスクリプタを取得する 'Set wmiFileSecSetting = GetObject( _ "winmgmts:Win32_LogicalFileSecuritySetting.path='" & HomePath & "'") Set wmiFileSecSetting = GetObject( _ "winmgmts:\\" & strComputer & "\root\cimv2:Win32_LogicalFileSecuritySetting='" & _ HomePath & "'") RetVal = wmiFileSecSetting.GetSecurityDescriptor(wmiSecurityDescriptor) If (RetVal <> 0) Then MsgBox "GetSecurityDescriptorに失敗しました:" & RetVal Exit Function End If 'ディクショナリにDACLを転記する Set DictACE = CreateObject("Scripting.Dictionary") For i = LBound(wmiSecurityDescriptor.DACL) To UBound(wmiSecurityDescriptor.DACL) If (Not wmiSecurityDescriptor.DACL(i).AceFlags And 16) Then Call DictACE.Add(i, wmiSecurityDescriptor.DACL(i)) End If Next '設定する新しいACEオブジェクトを最後に足す Call DictACE.Add("NewUser", wmiACE) 'DACLに書き戻す wmiSecurityDescriptor.DACL = DictACE.Items '対象フォルダのセキュリティデスクリプタを設定する RetVal = wmiFileSecSetting.SetSecurityDescriptor(wmiSecurityDescriptor) If (RetVal <> 0) Then MsgBox "SetSecurityDescriptorに失敗しました:" & RetVal Exit Function End If SetSecurityHomeDirectory = True End Function
最初の引数は,ドメインコントローラのサーバ名を指定する,2つめの引数はhomeフォルダが存在するサーバ名,3つめの引数はユーザID,4つめの引数はhomeフォルダのサーバ上での絶対パスを指定する。
オリジナルではサーバ名は一つ渡せばよい構造だったが,当方の場合ではドメインコントローラとファイルサーバが別物だったので引数が増えた。改造したのはその部分のみ。
セキュリティディスクリプタを取得する時には,UNCではうまく動作しなかったので,サーバ上でのローカルパス名を渡さなければならない。
削除の方は登録よりも簡単。
Sub cmdDelUsers_Click() Dim vID As String Dim iRow As Integer Dim ADSI As CADSI Set ADSI = New CADSI ADSI.OpenDomain "hogeserver.hogedom.local", "dc=hogedom,dc=local", "hogedom\administrator", "password" iRow = 2 vID = Cells(iRow, 3) Do While Len(vID) > 0 'Debug.Print vID ADSI.DeleteUser vID, "ou=Persons" RemoveFolder vID iRow = iRow + 1 vID = Cells(iRow, 3) Loop Set ADSI = Nothing End Sub Sub RemoveFolder(AID As String) Dim FSO As FileSystemObject Set FSO = New FileSystemObject FSO.DeleteFolder "\\hogeserver\home\" & AID, True Set FSO = Nothing End Sub
ユーザをドメインから削除した後に,ホームディレクトリを削除しているだけ。
ここで紹介するlogon.vbsは,共有フォルダをドライブ名に割り当てる作業や,ネットワークプリンタに接続する作業をログイン時に自動的に行うことを主な目的とするスクリプトです。ユーザの所属するグループによってネットワークドライブの割り当てを変更したり,コンピュータによって接続するネットワークプリンタ や通常使うプリンタを変更したりできるようになっています。
これらの機能でクライアントコンピュータの初期設定や設定変更が格段に楽になります。
logon.vbsの初期のバージョンは機能も少なく動作も簡単なものでした。グループによってネットワークドライブやプリンタの割り当てができる だけのものでした。それが各部署の要望を入れたりしているうちに多くのモディファイを受けてきました。大幅な設定変更が必要だったときに,すでに接続されているネットワークドライブの接続先をうまく変更できなかったために作業工数が増えてしまったことがあり,そのときに特に大きな機能アップを果たしました。現在では,ログイン時にネットワークドライブとプリンタの設定を自動化するスクリプトとして十分な機能を持ったものになっていると思います。
logon.vbsはADSIやWMIなどVBScriptで利用可能なさまざまなオブジェクトの利用方法の格好のサンプルとしても役立ちます。さほどきれいなコードではありませんが,コメントもついていますので,他のスクリプトを書くときの参考にしてください。
logon.vbsは,設定情報を記述したテキストファイル(マップファイルといいます)を解釈して実際の作業を行います。マップファイルのファイ ル名はmapfile.txtです。マップファイルの置き場所はlogon.vbsと同じフォルダですが,ローカルコンピュータの Windowsフォルダに置くこともできます。 logon.vbsは最初にローカルコンピュータの Windowsフォルダにマップファイルがあるかどうかをチェックし,そこにマップファイルがあったらそのファイルの中身を解釈して実行します。 Windowsフォルダにマップファイルがなかったら,logon.vbsと同じフォルダにあるマップファイルを解釈して実行します。
一般的にはlogon.vbsと同じフォルダにあるマップファイルに通常の設定が記述されていてWindowsフォルダにはマップファイルを置かな い運用が普通です。特別な設定が必要なコンピュータの場合や,あるコンピュータを一時的に違う設定にしたいような場合だけWindowsフォルダにマップ ファイルを設置するという利用法になると思います。
マップファイルはタブ区切りのテキストファイルです。1行は5列に区切られそれぞれの列は次のような意味を持ちます。
1列目 | 対象オブジェクトの種類 |
2列目 | 対象とするオブジェクト |
3列目 | コマンド |
4列目 | パス名など,コマンドに与えるパラメータ |
5列目 | オプション パラメータ |
1列目~2列目で指定したオブジェクトに対して,3列目のコマンドを実行します。コマンドにパラメータが必要な場合は4列目~5列目にパラメータを指定します。
コマンド行の解釈には次のルールが適用されます。
対象オブジェクトとは,そのコマンドを実行する条件指定のようなものです。スクリプトを実行しているユーザやコンピュータが所属するグループやコン ピュータ名によってコマンドの実行を制御することができます。1列目にはオブジェクトの種類を指定し,2列目で条件を指定します。
1列目 | 2列目 | 対象オブジェクトの種類 |
all | *を指定 | 全て。これが指定された行のコマンドは必ず実行されます。条件は存在しないので2列目の内容は無視されますが,記述ミスを防ぐために2列目には*を入れることを推奨します。 |
user | ユーザ名 | ユーザ。ログインしたユーザ名。ユーザ名の末尾に*を付けることで前方一致での指定が可能です。ユーザ名が一致した場合にコマンドは実行されます。 |
group | グループ名 | グループ。ログインしたユーザが所属するグループ。ユーザがそのグループに所属している場合にコマンドは実行されます。 |
pc | コンピュータ名 | コンピュータ。コンピュータ名の末尾に*を付けることで前方一致での指定が可能です。コンピュータ名が一致した場合にコマンドは実行されます。 |
ip | IPアドレス範囲 | IP アドレス。2列目には 10.20.30.0/255.255.255.0の形式で,ネットワークアドレスとサブネットマスクを指定することでIPアドレスの範囲を指定できま す。単一のIPアドレスを指定する場合はサブネットマスクを省略できます。詳しくはIPアドレス指定時の動作を参照してください。 |
3列目 | 4列目/5列目 | 動作 |
alert | 4列目:表示するメッセージをセットします。 | ポップアップメッセージを表示します。条件指定のテストなどに利用します。 |
exec | 4列目:実行するコマンドをセットします。 | 外部コマンドを実行します。 |
exit | 4列目以降のパラメータは必要ありません。 | 処理を終了します(以降のマップファイルを評価しません)。 includeコマンドでインクルードされたマップファイル中でexitコマンドに遭遇するとそのファイルの解釈を終了して呼び出し元のマップファイルに戻ります。 |
prn | 4列目:共有プリンタのUNCパスを指定します。 5列目:forceを指定すると強制的に再接続します。delete を指定すると割り当てを解除します。詳しくはネットワークプリンタ接続時の動作を参照してください。 |
ネットワークプリンタに接続します。 |
defprn | 4列目:共有プリンタのUNCパスを指定します。 | 通常使うプリンタを指定します。 |
include | 4列目:マップファイル名。 | 別のマップファイルをインクルードします。このコマンドを使って大きな条件毎にマップファイルを分割することができます。詳しくはマップファイルの分割を参照してください。 |
log | 4列目:onを指定すると以降の成功ログを記録します。offを指定すると以降の成功ログを記録しません。 | 成功ログの記録モードを設定します。この機能は主に動作確認に用います。詳しくはイベントログの記録を参照してください。 |
<ドライブ名>: | 4列目:共有フォルダのUNCパスを指定します。 5列目:deleteを指定するとそのドライブの割り当てを解除します。 |
共有フォルダをネットワークドライブに割り当てます。詳しくはネットワークドライブの割り当て時の動作を参照してください。 |
all * prn \\sv1\LaserPrinter1
無条件で共有プリンタの\\sv1\LaserPrinter1に接続します。
group kaikei Z: \\sv1\kaikei
ユーザがkaikeiグループに所属する場合に共有フォルダ\\sv1\ kaikei をZ:ドライブに割り当てます。
pc jimu* defprn \\sv1\JimuPrinter
コンピュータ名がjimuで始まる場合に\\sv1\JimuPrinterを通常使うプリンタとします。
IPアドレスによる振り分けは,コンピュータのIPアドレスを取り出すのにやや時間がかかるので,処理速度に影響を与えます。
ip 10.20.30.0/255.255.255.0 defprn \\sv1\KaikeiPrinter
IPアドレスが10.20.30.1~10.20.30.254の範囲内の時に, \\sv1\KaikeiPrinterを通常使うプリンタとします。
ip 10.20.30.40 defprn \\sv1\KaikeiPrinter
IPアドレスが10.20.30.40の時に,\\sv1\ KaikeiPrinterを通常使うプリンタとします。サブネットマスクを省略するときは上記のように/(スラッシュ)も記述しないようにします。
IPアドレスによる振り分けには,そのコンピュータに搭載されているアダプタのうち最初にリストアップされたIPアドレスを根拠にします。よって複 数のNICを持つ場合や,仮想的なネットワークアダプタを持つような場合には,正しい IPアドレスが取り出されるかどうか不確実な部分があります。そういう状況でないコンピュータしか対象にしない場合などに用途を限定して使ってください。
Windowsの「プリンタとFAX」の画面からネットワークプリンタに接続した場合は,コンピュータの電源を切っても,その接続の定義は残るよう になっていて,次回起動時もそのプリンタを使用することができます。起動する度に毎回プリンタへの接続をする必要はありません。
logon.vbsを使って接続が定義されている場合も同様に動作します。prn コマンドでプリンタへの接続を処理する場合は,ローカルですでに接続されている共有プリンタである場合は再度プリンタ接続することはしません。通常はこの動作で必要な結果が得られます。しかしプリンタの構成変更時などで何らかの理由で接続済みのプリンタにも再度接続動作をさせたい場合は5列目にforce と記述します。そうすることで接続済みのプリンタであっても再度接続します。
古いプリンタを廃棄した場合や,プリントサーバを別のものにした場合など,すでに接続されているネットワークプリンタを削除する必要がある場合 は,5列目にdeleteオプションを記述します。すると次のログインの時にそのプリンタは削除されます。deleteオプションを記述した行はしばらく の間運用して,対象となるほぼすべてのコンピュータがログオンした時期が過ぎたら,マップファイルから消してしまっていいでしょう。
このコマンドの場合だけはコマンドではなくドライブ名を記述します。ドライブ名は英数字1文字+:(コロン)です。プリンタと同じように一度接続さ れたネットワークドライブの割り当ては,ログオン時に再接続されます。 logon.vbsでは,ネットワークドライブ接続のコマンドがあった場合は次のような動作をします。
すでに接続されているネットワークドライブを切断するだけの場合には,5列目に deleteオプションを記述します。deleteオプションを記述した行はしばらくの間運用して,対象となるほぼすべてのコンピュータがログオンした時 期が過ぎたら,マップファイルから消してしまっていいでしょう。
logon.vbsは,スクリプト実行中にエラーが発生するとイベントログにメッセージを記録します。logon.vbsの動作がおかしい場合はク ライアントのイベントログを見てエラーが記録されていないかどうか確認します。基本的にエラーが記録されるというのは動作が不正である言うことですので, 原因を探りマップファイルや場合によってはlogon.vbsを修正する必要があります。
それとは別にマップファイルの記述やlogon.vbs自体の動作確認をする時に,成功したログも記録したいことがあります。成功ログを記録するようにするには,マップファイルの先頭付近に次の行を記述します。
pc <ComputerName> log on
<ComputerName>の部分は,動作確認を行うコンピュータ名を記述します。上記のように記述すると1行解釈して実行する毎に成功した場合でもログがイベントログに残るようになります。イベントビューワでログの内容を参照して動作確認に役立てます。
includeコマンドを記述すると,別のマップファイルを解釈して実行します。読み込んだマップファイルの処理が終了すると,元のマップファイルの次の行から実行が再開されます。
マップファイル名は,絶対パスまたはスクリプトファイルが置かれているフォルダからの相対パスで指定できます。
group kaikei include mapsub/kaikei.txt
上記の例では,kaikeiグループに所属しているユーザの場合は,mapsub フォルダにあるkaikei.txtをマップファイルとして解釈して実行します。この場合は相対パスが指定されていますので,logon.vbsが置かれ ているフォルダにmapsubフォルダがあって,その中にkaikei.txtがなければなりません。
マップファイルをincludeして分割する利点はいくつかあります。
logon.vbsのダウンロードはこちら
添付 | サイズ |
---|---|
logon.vbs | 15.23 KB |
Windowsのログオン状況を記録する必要がある場合,ドメインコントローラでログオンイベントの監査で「成功の監査」を設定する方法が考えられ ますが,これはあるPCからドメインにログオンした時だけではなく,本当にログオフするまでの間に何度となくログオン,ログオフが記録されます。実際にロ グオン動作をしたとかログオフしたとかを正確に記録することはできません。
グループポリシーの中のユーザーの構成/Windowsの設定/スクリプトにログオンの時と,ログオフの時に実行するスクリプトを設定することができます。そこで,簡単なスクリプトを動かして,ログオンとログオフの記録を取る方法を考えました。
スクリプトの仕様としては,次のようなフィールドを持つCSV形式のレコードをログファイルに追加書き込みすることとします。
コラム | フィールド内容 | 備考 |
1 | イベント | ログオンであるかログオフであるか |
2 | 時刻 | 日付と時刻 |
3 | コンピュータ名 | ログオン(オフ)したコンピュータ名 |
4 | ユーザ名 | ログオン(オフ)したユーザ名 |
5 | ドメイン名 | |
6 | IPアドレス | 複数ある場合は;で区切ってそのPCの全てのIPを記録 |
3~6のデータをについてはWMIを使って取得し,FileSystemObjectを使ってテキストファイルに出力します。
'ログオン・ログオフイベントを記録するスクリプト sEvent = "LogOn" 'ログオフ用はここを変更する sFileName = "\\server01\log\logon" & _ Year(Now) * 100 + Month(Now) & ".log" On Error Resume Next Set FSO = CreateObject("Scripting.FileSystemObject") Set wshNetwork = CreateObject("WScript.Network") SearchPC = wshNetwork.ComputerName Set objComputer = GetObject("winmgmts:{impersonationLevel=impersonate}!//" & SearchPC) Set NICs = objComputer.ExecQuery("select * from Win32_NetworkAdapterConfiguration") For Each Adapter In NICs if UCase(Adapter.DNSHostName) = UCase(SearchPC) then IPAddress = Adapter.IPAddress IPSubnet = Adapter.IPSubNet DefaultIPGateway = Adapter.DefaultIPGateway sIP = "" sSubNet = "" sGateway = "" If IsArray(IPAddress) Then For I = LBound(IPAddress) To UBound(IPAddress) sIP = sIP & IPAddress(I) & ";" sSubNet = sSubNet & IPSubnet(I) & ";" sGateway = sGateway & DefaultIPGateway(I) & ";" Next sMAC = Adapter.MACAddress sWINS1 = Adapter.WINSPrimaryServer sWINS2 = Adapter.WINSSecondaryServer End If exit for end if Next sMsg = sEvent & "," & _ Now & "," & _ wshNetwork.ComputerName & "," & _ wshNetwork.UserName & "," & _ wshNetwork.UserDomain & "," & _ sIP set oText = FSO.OpenTextFile(sFileName, 8, true, 0) oText.WriteLine(sMsg) oText.Close
このスクリプトをポリシーのログオンスクリプトに設定し,スクリプトの先頭にあるsEventをLogOffに変えたものをログオフスクリプトに設 定します。sFileNameは,Logを記録する共有フォルダのUNCを指定します。この例ではファイル名の指定で月ごとにファイルが変わるようにして あります。
Active Directory のドメインに参加しているPCのホスト名をある理由から変更しなければならなくなった。それも数十台。現場に行って作業をするのは面倒なので,スクリプトを書くことにしました。
ホスト名の変更は,Win32_ComputerSystem クラスの rename メソッドで変更できます。このメソッドを実行するには,ローカルPCに対して管理者権限を持っていなければなりません。今回の対応先では,Domain Users がローカルのAdministratorsグループに入っているので,その点は大丈夫です。ですがドメインに入っているPCのホスト名を変更するのですから,Domain Admins の権限が必要になります。ユーザーに管理者のユーザー情報を教えるわけに行きませんので,それはスクリプトに埋め込みたいところです。rename メソッドにドメインの管理者のユーザー情報を渡してやれますので,それを利用します。
古いホスト名を新しいホスト名にするわけですから,その対応表が必要になります。ここでは,WSHのDictionaryオブジェクトを使うことにしました。このオブジェクトはVBSで連想配列を実現できます。
'Dictionaryオブジェクトの生成 Set dicMap = CreateObject("Scripting.Dictionary") '値を追加 dicMap.Add "キー", "値"
Dictionaryは上記のように使います。値を取り出すときは
Value = dicMap("キー")
という感じで取り出します。これを使って,キーに旧ホスト名を値に新ホスト名をセットした連想配列を作り,それを検索してホスト名の変更をするスクリプトにします。
'rename.vbs 'Copyright (c) 2009. Sunvisor. All rights reserved. '**Start Encode** 'Message String Const MSG = "情報処理室まで連絡してください。" 'Domain Administrator Account sUsername = "administrator" sPassword = "adminpass" Set dicMap = CreateObject("Scripting.Dictionary") 'Define Convert Table dicMap.Add "old001", "new001" dicMap.Add "old002", "new002" dicMap.Add "old003", "new003" dicMap.Add "old004", "new004" dicMap.Add "old005", "new005" dicMap.Add "old006", "new006" dicMap.Add "old007", "new007" 'Get WMIService Object Set objWMIService = GetObject("Winmgmts:root\cimv2") For Each objComputer in objWMIService.InstancesOf("Win32_ComputerSystem") sOldName = LCase(objComputer.Name) 'Current Computer Name sNewName = dicMap(sOldName) 'New Computer Name if sNewName = "" Then WScript.Echo "このコンピュータ(" & sOldName & _ ")の新しい名前が定義されていません。" & vbcrlf & MSG Else '実際に変更する Return = objComputer.rename(sNewName,sPassword,sUsername) If Return <> 0 Then WScript.Echo "名前の変更に失敗しました。エラーコード = " & Return _ & vbcrlf & MSG Else WScript.Echo "コンピュータ名を変更しました。" _ & vbcrlf & " コンピュータを再起動してください。" End If End If Next
ロジック的にはこれでいいんですが,このスクリプトをエディタで開かれると,7~8行目に書いてある管理者のアカウント情報がばれてしまいます。このスクリプトをエンコードして配布することにします。Windows Script Encoder をダウンロードしてインストールします。
C:\Program Files\Windows Script Encoder>screnc d:\Script\rename.vbs d:\Script\rename.vbe
とすると,スクリプトをエンコードできます。エンコードは全角文字で記述された日本語はそのまんまでしたので,ユーザーから隠したい情報を日本語で書かないようにしないといけません。
一部のPCで,renameメソッドが1219というエラーを返しました。これは,
ERROR_SESSION_CREDENTIAL_CONFLICT
同じユーザーによる、サーバーまたは共有リソースへの複数のユーザー名での複数の接続は許可されません。サーバーまたは共有リソースへの以前の接続をすべて切断してから、再試行してください。
というエラーで,なぜにこれがでたのかよくわかりません。
職場の環境で,WINSサーバのアドレスが変更になりました。一人ひとりにやってもらわなければなりませんが,説明が大変ですしちゃんとやってくれるかどうか不安です。そこでWINSの設定変更をするスクリプトを作成しました。
strComputer = "." sPrimaryWins = "192.168.1.10" sSecondaryWins = "" 'アダプタ情報を得る Set objTargetAdapter = GetAdapter If not objTargetAdapter is nothing Then 'アダプタにWinsをセットする objTargetAdapter.SetWINSServer sPrimaryWins , sSecondaryWins '設定情報を再取得する Set objTargetAdapter = GetAdapter sMsg = "WINSサーバーの設定を完了しました" & vbcrlf & _ " Primary WINS server: " & objTargetAdapter.WINSPrimaryServer & vbcrlf & _ " Secondary WINS server: " & objTargetAdapter.WINSSecondaryServer Else sMsg = "デフォルトゲートウェイが設定されているアダプタが見つからなかったため" & vbcrlf & _ "Winsの設定ができませんでした" End If WScript.Echo sMsg Function GetAdapter() Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colAdapters = objWMIService.ExecQuery _ ("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True") Flag = False For Each objAdapter in colAdapters If Not IsNull(objAdapter.DefaultIPGateway) Then Set GetAdapter = objAdapter Flag = True End If Next if Not Flag Then GetAdapter = nothing End if End Function
ネットワーク・アダプタが複数ある場合があるので,デフォルトゲートウェイが設定されているアダプタにWINSを設定することにしました。そこで,対象となるアダプタを得るのがGetAdapter関数です。このスクリプトではWINSを設定後,再度Win32_NetworkAdapterConfigurationオブジェクトを取得し直して,設定後のWINS情報を表示させています。
wshでは,wshNetworkのEnumPrinterConnectionsでネットワークプリンタの共有名を得ることができるとあるのだが,実際にプリンタの共有名を調べてみるとちょっとおかしな感じだと言うことがわかった。
プリンタ名称 AAA Printer PRN2000 サーバ名 prnsv 共有名 prn_1
上記のような共有プリンタをクライアントに割り当てていた場合には,
\\prnsv\prn_1
このプリンタの共有名というのは,上記のようにになるはずなのに,EnumPrinterConnectionsでは,
\\prnsv\AAA Printer PRN2000
という共有名が返ってくる。
'WSHでのプリンタの共有名列挙 Set WshNetwork = WScript.CreateObject("WScript.Network") Set oDrives = WshNetwork.EnumNetworkDrives Set oPrinters = WshNetwork.EnumPrinterConnections sP = "" On Error Resume Next For i = 0 to oPrinters.Count - 1 Step 2 sP = sP & "ポート " & oPrinters.Item(i) & " = " & oPrinters.Item(i+1) & vbcrlf Next WScript.Echo sP
本当の共有名を得るにはWMIを使わなくてはならないようだ。
'WMIでの共有名列挙 strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_Printer",,48) sR = "" For Each objItem in colItems sShareName = objItem.ServerName & "\" & objItem.ShareName if Len(sShareName)>0 then sR = sR & sShareName & ";" End If Next WSCript.Echo sR
運用しているサーバで,Active Directoryのドメインサーバ間でのレプリケートができなくなっていた。しょうがないので,NETLOGONに保存されているLogonスクリプト に変更があった場合には,二つのサーバのファイルを更新していた。しかしそれではめんどうだし,エラーが発生しているまま放置しているのも気持ちが悪いの で,本気で直すことにした。
出入りのメンテナンス業者のエンジニアに聞いたところ,サーバを設置した後,ネットワーク機器の接続を変更した りした場合にそのようなことが起きることがあるという。サーバ間のレプリケートの接続設定は,2台目のドメインサーバが追加されたときに自動設定される。 そのごネットワーク機器の変更があると,相手先のサーバが見つからない状態になってしまうらしい。まずはその対策を施すことにした。
これはつまり,自動設定だった接続の設定を手動で設定し直してやるという作業。ここまで作業して帰宅し,翌日確認したが,レプリケートは失敗していた。
Server1の方にはイベントID 13508の次の警告が記録されている。これは以前のまま。
ファイル レプリケーション サービスの問題のため、DNS 名 Server2.domain.local を使用して c:\windows\sysvol\domain に対して Server2 から Server1 へのレプリケーションを有効にできません。 再実行します。 この警告が表示される理由として次のいくつかが考えられます。 [1] FRS によりこのコンピュータから DNS 名 Server2.domain.local を正しく解決できません。 [2] FRS が Server2.domain.local 上で実行されていません。 [3] このレプリカに対する、Active Directory のトポロジ情報が一部の ドメイン コントローラにまだ レプリケートされていません。 このイベント ログ メッセージは接続ごとに一度表示されます。問題が 解決されると、接続が確立され たことを示す別のイベント ログ メッセージが 表示されます。
Server2の方のログを見る。こちらはID 13568のエラーが記録されている。これも以前から記録されているログである。結構長い解説が書いてある。内容をよく読んでみる。
ファイル レプリケーション サービスにより、レプリカ セット "DOMAIN SYSTEM VOLUME (SYSVOL SHARE)" が JRNL_WRAP_ERROR の状態であることが検出されました。 レプリカ セット名 : "DOMAIN SYSTEM VOLUME (SYSVOL SHARE)" レプリカ ルート パス : "c:\windows\sysvol\domain" レプリカ ルート ボリューム : "\\.\C:" NTServer USN ジャーナルから読み取ろうとしているレコードが見つからない場合に、レプリカ セット は JRNL_WRAP_ERROR の状態になります。これは次の理由のいずれかによって 発生します。 [1] ボリューム "\\.\C:" がフォーマットされています。 [2] ボリューム "\\.\C:" の NTServer USN ジャーナルが削除されています。 [3] ボリューム "\\.\C:" の NTServer USN ジャーナルが切り詰められています。 ジャーナルの最後 で壊れたエントリが見つかった場合は、Chkdsk でジャーナルを切り詰めることができます。 [4] このコンピュータでファイル レプリケーション サービスが長い間実行されていませんでした。 [5] ファイル レプリケーション サービスでは、"\\.\C:" のディスクの I/O 活動速度についていくこ とができませんでした。
うちの場合は,[4]あたりが原因だろうか。
"Enable Journal Wrap Automatic Restore" レジストリ パラメータを 1 に設定 すると、このエラー状態 から自動的に回復するために、次の回復操作が行われ ます。 [1] 5 分後に行われる最初のポーリングのときに、このコンピュータはレプリカ セット から削除されま す。5 分間待機したくない場合は、"net stop ntfrs" を実行して から "net start ntfrs" を実行し、 ファイルレプリケーション サービスを再起動してください。 [2] 削除後のポーリングのときに、このコンピュータはレプリカ セットに再度追加されます。 コン ピュータが再度追加されると、レプリカ セットに対して完全なツリーの同期が行われます。 警告: 回復プロセス中は、レプリカ ツリーのデータは利用できない可能性があります。 このエラー状態 が再度発生する場合は、自動回復により途中でデータが 利用不可になるのを防ぐために、上記のレジス トリ パラメータを 0 にリセットする 必要があります。 このレジストリ パラメータを変更するには、"regedit" を実行します。 [スタート] ボタンをクリックし、[ファイル名を指定して実行] をクリックしてから "regedit" と入力 します。 HKEY_LOCAL_MACHINE を展開し、 キーのパス: "System\CurrentControlSet\Services\NtFrs\Parameters" を開いて、値名: "Enable Journal Wrap Automatic Restore" をダブル クリックしてから値を更新します。 値名が存在しない場合、[編集] メニューにある [新規] から [DWORD 値] で追加することが できます。 上記の値名を正しく入力してください。
おっしゃるとおりにやってみることにした。
net stop ntfrs net start ntfrsを実行してntfrsサービスを再起動した。
そうすると,Server2のイベントには,エラーログは記録されずに,いくつかの警告ログが記録された。その中に次のログがあった。変化があったようだ。
ファイル レプリケーション サービスでは、別のドメイン コントローラのデータでシステム ボリューム を初期化しています。この処理が完了するまでコンピュータ Server2 をドメイン コントローラ にするこ とはできません。それからシステムボリュームは SYSVOL として共有されます。 SYSVOL の共有を確認するには、コマンド プロンプトで次のコマンドを実行してください: net share ファイル レプリケーション サービスで初期化の処理が完了すると、SYSVOL の共有が表示され ます。 システム ボリュームの初期化には時間がかかる可能性があります。 この時間はシステム ボリュームの データ量、 ほかのドメイン コントローラの稼働状態、および ドメイン コントローラ間のレプリケー ション間隔によります。
なんだか終わりに近づいてきた感じ。この文章の通りにnet shareで共有を確認した。管理共有や公開フォルダは表示されるが,SYSVOLやNETLOGON共有は表示されなかった。しばらくしてもう一度確認すると
H:\>net share
共有名 リソース 注釈 ------------------------------------------------------------------------------- (中略) NETLOGON C:\WINDOWS\SYSVOL\sysvol\domain.local\SCRIPTS Logon server share SYSVOL C:\WINDOWS\SYSVOL\sysvol Logon server share
のように,NETLOGINやSYSVOLの共有が表示された。いいじゃないですか。その後,Server1にて次の作業を行った。
NETLOGON共有で確かめてみると,同期がとれていた。その後,レプリケートのエラーは発生していない。
とあるサーバ,[sv1]のファイル共有を,とある理由で別名[alias1]というサーバ名で接続したい。これは簡単だと思った。DNSにCNAMEレコード[alias1]を作って,ターゲットホストを[sv1.domain]とすればいいと思った。しかし・・・
エクスプローラのアドレス欄に[\\alias1]と入力してみると,「alias1が見つかりません。」とのつれないお言葉・・・
それでは,と,ファイル名を指定して実行から[\\alias1]を入力してみると
ネットワークに重複した名前があるため接続されませんでした。コントロールパネルのシステムでコンピュータ名を変更してから再実行してください。
という意味不明(だって重複してないし・・・)なメッセージが出てサーバの共有を表示できません。
これを解決するには,公開しているファイルサーバ上でレジストリに値を設定する必要がありました。
以上で,[alias1]でファイル共有にアクセスしても接続できるようになります。
Windows 2000 ベースのサーバー上の SMB 共有へのエイリアス名による接続が機能しない
上記のページは「Windows 2000の」となっているが,原文ページでは
Windows 2000-based computer or a Windows Server 2003-based computer
となっているので,2003も対象となります。2003の場合上記文書の「修正プログラム」の適用は必要ありません。
セキュリティを確保するために,Windowsクライアントをログインした状態のまま一定時間操作がない場合にロックさせたい。こういった要望は普通にあることだと思います。
ローカルPCでこれをするには,コントロールパネル > 画面 > スクリーンセーバー にて,
で,PCがロックされるようになりますが,ちょっと知っている人は,「うっとおしい」と言ってこの設定を外してしまいます。これをさけるためグループポリシーで強制し,ユーザーが変更できないようにしたいと思います。
「スクリーン セーバーの実行可能ファイル名」も指定すべしということも聞きます。これは,パスワードによる保護に対応していないスクリーンセーバーがあるからそのように記述しているのでしょうか。私が管理しているところ場合は,お気に入りのスクリーン セーバーを使っている方が多いので,この設定は「未構成」のままにしています。
Active Directory を他のWebサービスのLDAP認証サーバーとしても利用しているような場合。Webサービスは使ってもらいたいが,パソコンにログオンして欲しくはないユーザーというのがあると思います。そういう人をログオンさせないようにするには,どうしたらよいでしょうか。
ログオンさせたくないユーザーはだれなのかを判断しなければなりませんから,まずはグループを作ります。ここでは,冒頭の例に沿ってldapusersというグループを作ります。そして,パソコンにログオンさせたくないユーザーをこのグループに所属させます。
グループポリシーに「ローカルでログオンを拒否する」というポリシーがあります。このポリシーにグループを設定します。
別エントリで,wpadに関するネタを書きました。
ドメインコントローラーをWindows Server 2003から2008 R2にリプレイスしました。そうしたら,「設定を自動的に検出する」と設定している多くのPCでインターネットに接続できないトラブルが発生しました。なぜこんなことが起きたのか調べてみると...
ドメインコントローラーは当然DNSを兼ねています。そのDNSでwpadを引いてみると,そのDNSから結果が返ってこないのです。フォワーダにしてあるDNSにもwpadのレコードがあったため,そちらのサーバーから返ってきたFQDNが返ってきています。試しにドメインのサフィックスをつけてnslookupでDNSを引いてみると,「レコードがない」とのお返事です。でもDNSサーバーのレコードを見てみるとwpadというレコードはあります。
なぜ,アドレスが返ってこないんだろう。
いろいろ試行錯誤しても理由がわかりませんでしたが,DNSのイベントログを見てみると
グローバル クエリ禁止リストは、特定のホスト名の DNS クエリをブロックすることにより、ネットワーク への攻撃を防止する機能です。この機能によって、この DNS 名のデータが DNS データベースに存在するに もかかわらず、DNS サーバーによる wpad.hoge.local. のクエリがエラー コード NAME ERROR で失敗しま した。ローカルの権威あるすべてのゾーンで、この禁止リストのラベルで始まる他の名前のクエリも失敗し ますが、このコンピューターの DNS サーバー サービスが再開されるまでは、他のクエリがブロックされて もイベントは記録されません。この機能の詳細と構成方法については、製品のドキュメントを参照してくだ さい。
というのがありました。これだと思い。「グローバル クエリ禁止リスト」をググってみてわかりました。
Windows Server 2008 では,wpadに対する問い合わせを拒否するのがデフォルトになっているのです。なんじゃそら。では,これを解除してwpadへのお返事をちゃんとさせるには,次のようにします。
1. Server上で,コマンドプロンプトを「管理者として実行」します。
2. 禁止リストの一覧を見てみます。リストの中にwpadが入っています。
C:\dnscmd localhost /info /globalqueryblocklist クエリ結果: 文字列: wpad 文字列: isatap
3. 禁止リストからwpadをはずしたものを再設定します。ここではisatapを残すように指定しています。
C:\dnscmd localhost /config /globalqueryblocklist isatap
以上で,wpadが引けるようになりました。グローバル クエリ禁止リスト自体を無効にすることもできるようです。
参考サイト
USBメモリを介して広がるウィルスがはやっております。そういうウィルスはUSBメモリにautorun.infを作成して,感染を広げていきます。その対策としては,同じ名前のフォルダをUSBメモリに作成してしまって,autorun.infファイルを作成できないようにするというのが有効だそうです。その作業をやってもらいたいんだけど説明してわかってもらうのが大変。そこで簡単にやってもらおうというスクリプトを作りました。
Dim FSO Dim flag Set FSO = CreateObject("Scripting.FileSystemObject") flag = False For Each Drv In FSO.Drives 'Dドライブ以降のリムーバブルディスクが対象 If Drv.DriveLetter > "C" And Drv.DriveType = 1 Then If Drv.IsReady Then Flag = True MakeAutorunFolder(Drv) End If End If Next If Not Flag Then MsgBox "対象となるドライブが見つかりませんでした。" & _ "USBメモリを接続して再度やり直してください", vbInformation End If Sub MakeAutorunFolder(ADrive) sFileName = ADrive & "\autorun.inf" sMsg = ADrive & "ドライブのディスクに対策を施しますか?" If MsgBox(sMsg, vbYesNo+vbQuestion, "作成") = vbYes Then ' WScript.Echo "実行します" If FSO.FolderExists(sFileName) Then ' WScript.Echo "処理の必要はない。" Else If FSO.FileExists(sFileName) Then ' WScript.Echo "autorun.infを削除します。" FSO.DeleteFile sFileName, True End If ' WScript.Echo "autorun.infフォルダを作ります" Set f = FSO.CreateFolder(sFileName) f.Attributes = f.Attributes+3 End If End If End Sub
このスクリプトでは,Dドライブ以降のリムーバブルディスクを見つけると対策を施すかどうかメッセージを表示します。Yesを選択すると,autorun.infファイルがあればそれを削除してからautorun.infフォルダを作成します。
サーバー上にあるファイルをエクスプローラーで右クリックするとなんか遅い,20秒程度待たされる,という現象が発生しました。あるサーバーではOKだけど,あるサーバーでは発生します。原因は何なのかググってみると,WebClientが原因のようでした。
実は,以前に他の問題でWebClientが原因であると言うことがありました。ログオンに時間がかかるというものです。
Windows XP を実行するコンピュータでドメインにログオンするとき、またはネットワーク リソースに接続するときに、時間がかかる
そのときは,ドメインコントローラーにてIISをたてて,Port80へのアクセスにとりあえず返事をさせるという方法で解決をしました。WebClientがWebDAVでの接続を試みてもIISでPort80に対してすぐにレスポンスしてやればいい,という発想です。各クライアントのサービスを停止するのもなぁ,と思いましたので。
その後,我が職場のサーバーも数が増えて,別のサーバーではIISも立ち上げておらず,WebClientがPort80のタイムアウトを待ってから接続するため,いろんなサーバーの最初の接続が遅くなっていたわけです。これは何か根本的な対策が必要だと言うことになりました。
WebClientサービスをポリシーで止めることにしようとググってみると,「犯人はWebClientサービス?」というページが見つかりました。これです。全く同じ現象。こちらのサイトでは,ログオン スクリプトでサービスを停止するという方法をとっておられましたが,僕はポリシーでやってみることにしました。
グループ ポリシー オブジェクト エディタ の左ペインのツリーで,
コンピュータの構成 > Windowsの設定 > セキュリティの設定 > システムサービス
を選択します。右ペインにサービス名がずらっと並びますので,WebClientをダブルクリックして,
「このポリシーの設定を定義する」にチェックを入れて「手動」を選択します。
これでドメインに参加している各クライアントに対してWebClientサービスを停止することができました。
同じことを,今度は2008 Serverがドメインコントローラーになっているドメインでやろうとしたところ,問題が発生しました。グループ ポリシー管理エディタの上記の場所にWebClientが存在しないのです。
これは2008 Server自体にWebClientサービスがないからなんだそうです。でもドメインのクライアントはほとんどがXpで,それらのWebClientサービスを停止したいのです。しかし一覧に存在しないことには,それを「無効」に設定することができません。どうしたものかと思っていたら次の記事を見つけました。
この中に
グループポリシーの作成はVistaで行い、確認をWindows Server 2008で行いました。
という記述がありました。クライアントでグループ ポリシー オブジェクト エディタを実行すれば,クライアントにあるサービスは表示されるということのようです。私のクライアント(OSはXp)にはWindows Server 2003のadminpakがインストール済みでしたので,ローカルにインストールされている「Active Directory ユーザーとコンピュータ」を起動し,対象のドメインに接続して,グループポリシーの編集まで行ってみると,そこにはWebClientポリシーが表示されています。表示されているのでとりあえずそれを無効に設定してみました。これで本当にポリシーが設定できたのでしょうか。もう一度サーバー上でサーバー マネージャを起動してポリシーを確認してみると,
ちゃんとWebClientの項目が追加され,「無効」になっています。
Windows 7をテスト導入してみて,業務用アプリケーションをインストールしてみたらちゃんと動作しませんでした。これが動作しないことにはどうしようもありません。そこで評判のXP Modeを試してみました。試したのはWindows 7 Proの製品版と,XP ModeのRCです。
Windows 7 活用術 | Windows XP MODE (入門編) | TechNet
上記のページの解説に従ってインストールすると,簡単にXP Modeは動作しました。
動作はしましたが,大きな問題が。
会社のPCで利用する場合,複数のアカウントでログオンすることが多いと思います。
しかしXP Modeは,ログオンするアカウントごとにバーチャルマシンが違うのです。あるアカウントでXP Modeのインストールをします。XP Modeで動くマシンの上に必要なアプリケーションをインストールして環境を整えます。そして同じPCに別のアカウントでログインして,XP Modeを実行すると,OSのインストールから始まるのです。最初のアカウントで設定したバーチャルマシンと全然別のマシンが作られると言うことなのでしょう。とうぜん,アプリケーションも一からインストールし直しです。
確かに,個人単位で環境を持てることはいいことかもしれませんが,業務で使っているレガシーなアプリケーションを使うためにあるのがXP Modeだと思いますが,これだと,PC台数×利用ユーザー数だけのインストール作業が待っているわけで,そういった用途には実は「全然向いていない」と言うことになるのではないでしょうか。
うちの勤務先でもADのドメインを作っています。PCはほぼ一人1台ですので普段ログインする人は同じですが,ドメインですから他のアカウントでログオンすることもあります。人事異動で利用者が変わることもあります。そのたびごとにXP Modeを作り直しですか?こりゃたまりません。
うちの勤務先での結論は「こりゃ,使えねぇ。別な方法考えなきゃな」に固まりつつあります。
サーバー機には2枚のNICがついているのが普通です。2枚目のNICを管理ネットワークとしてUPSとの通信などに使うという使い方をすることがあります。こういうのを裏LANと言ったりします。裏LANは,一般のネットワークからは隔離されている場合が多いと思います。この記事は,そういった裏LANを運用している状態で,Windowsのドメイン コントローラを設置する際に注意すべき点について記します。
Windowsのドメイン コントローラは通常DNSサーバーを兼ねます。
そして同じホスト名のAレコードが複数ある場合には,ラウンドロビンでDNSクエリーを受け取るたびに,違うアドレスを返します。これがラウンド ロビンです。裏LANを運用する場合,これが悪さをします。DNSサーバーに2枚のNICがありそれぞれにIPアドレスが割り当てられている場合,それらのアドレスは両方ともDNSに登録されます。これは上記の複数のAレコードがある場合にあてはまるので,DNSサーバーは2つのNICのアドレスを交互に返すことになります。
例えば,次のような設定の場合
ホスト名 | server01 | |
NIC1 | 192.168.1.100 | 一般のネットワーク(表LAN) |
NIC2 | 192.168.200.100 | 管理用ネットワーク(裏LAN) |
ラウンド ロビンが有効になり,DNSは一般ネットワークからのクエリーに対しても192.168.1.100を返したり192.168.200.100を返したりします。ところが192.168.200.100は裏LANで,一般のネットワークからアクセスできないのでserver01として引いたIPアドレスが2回に1回は接続不能なアドレスを返すという状況になってしまいます。
ラウンド ロビンはデフォルトで有効になっています。これを無効にするには,管理ツール > DNSを起動し,サーバ名を右クリックしてポップアップ・メニューの[プロパティ]を実行すると,次のようなダイアログが表示されます。[詳細]タブにある[サーバ オプション]でラウンドロビン機能の有効/無効を設定することができます。
それでも,DNSでserver01を引いたときには,裏LANのIPも返ってきます。裏LANのIPをDNSが返さないようにするには,同じダイアログの「インターフェイス」タブで設定します。
デフォルトではリッスン対象が上図のように「すべてのIPアドレス」になっています。これを「指定したIPアドレス」に変更して,裏LANのIPアドレスを削除します。
上記のようにするとDNSには裏LANのNICは登録されなくなります。この処理をする場合は,前項のラウンド ロビンの無効化は設定する必要はありません。
Windowsサーバーの共有フォルダにMacのOSXから比較的簡単にアクセスできますが,共有フォルダがドメインコントローラー上にあるとうまく接続できません。同じドメインに所属するサーバーでも単なるメンバサーバーであれば接続できるのに,ドメインコントローラーの場合は正しい認証情報を入力しても接続エラーが発生します。
これは,「Macintosh の SMB 接続は "SMB 署名" というものに対応していないらしく、そしてドメインコントローラではこれがデフォルトでは必須となるそうで、そのせいで認証に失敗してしまうということでした。」というのが原因だそうです。
そこで,参考サイトの記事の通り,ドメインコントローラーのセキュリティポリシーを変更することにしました。
コンピュータの構成 > Windowsの設定 > ローカルポリシー > セキュリティオプション
にある,「Microsoft ネットワーク サーバー: 常に通信にデジタル署名を行う」が「有効」になっているので,これを「無効」に変更します。
ポリシーをすぐさま反映させるために,gpupdateを実行します。
C:\>gpupdate
以上の操作をすることでOSXからもファイル共有に接続できるようになりました。
ずーっと以前Xerox製の複合機からスキャンする際,サーバー上のフォルダへプッシュする設定を試したときにも,メンバサーバー上のフォルダにはプッシュできるのに,ドメインコントローラーだとだめだったことがありました。今考えてみるとあれも,この件が原因だったのではないかと思います。
私の職場ではインターネットへ出るのにProxyサーバーを使用しなければ出ることはできません。しかしイントラにあるサーバーについては,当然ながらプロキシなしで接続する必要があります。そういう時には,自動構成スクリプトを作って,ブラウザはそれを解釈してネット接続するようにします。
そしてそれをさらに簡単にするためには,wpadというサーバーにwpad.datという名前で自動構成スクリプトを置けば,「設定を自動的に検出する」にチェックを入れるだけで,自動構成スクリプトを読み込んでくれるようになります。
実際にはwpadというサーバーを新たに立てる必要はなく,DNSで名前が解決できればいいので,DNSにwpadというレコードを登録するのと,DNSサフィックスをちゃんと指定して,単に wpad という名前でnslookupした時にちゃんと値が返るようにしなければなりません。
しかし,これらの設定をちゃんとやっても,なんかうまくプロキシ設定を読んでくれないことがあります。これまでそれが不思議でなりませんでした。
いろいろ調べてみると,DefaultConnectionSettingsとSavedLegacySettingsというレジストリエントリが関係していることがわかりってきました。IEはインターネット オプションのダイアログでプロキシ関係の設定がされた場合や,このエントリがない状態で最初にIEを起動する際に,このエントリを作ります。そしてこの中には,自動的に検出した自動構成スクリプトの場所を完全修飾の形式で,エントリのデータに保存するのです。そして保存されている自動構成スクリプトの場所が現実にそぐわなくなったような場合に,「設定を自動的に検出する」にチェックが入っていても,再度自動構成スクリプトの場所を自動的に検出しないみたいなんです。このエントリに余分な情報が入っているがために,うまくつながらない状態が修復できないのです。
だから,えいっとこのエントリをけしちゃおうというのが,次のスクリプトです。スクリプトを実行すると,DefaultConnectionSettingsとSavedLegacySettingsというレジストリエントリを消しちゃいます。IEは次に起動するときにこのエントリを勝手に作るので,消しちゃっても大丈夫です。
Set WshShell=WScript.CreateObject("WScript.Shell") Const KEYNAME="\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections\" CONST KEY1 = "DefaultConnectionSettings" Const KEY2 = "SavedLegacySettings" DeleteValue("HKLM" & KEYNAME & KEY1) DeleteValue("HKLM" & KEYNAME & KEY2) DeleteValue("HKCU" & KEYNAME & KEY1) DeleteValue("HKCU" & KEYNAME & KEY2) WScript.Echo "プロキシ設定をクリアしました" Sub DeleteValue(AReg) Dim sValue On Error Resume Next sValue = WshShell.RegRead(AReg) If Err <> 0 Then Err.Clear Else WshShell.RegDelete AReg End If End Sub
VBSで設定系のスクリプトをWindows 7などで実行すると,UACにひっかかって Access Denied あたりのエラーがでちゃってうまく動作しません。
これを回避する方法をいろいろ調べたのですが。次のサイトなどにあった情報で解決できました。
Windows VistaでのWSH(VBScript)の管理者権限への昇格方法
これらのサイトで採用している方法は,OSのバージョンなどを調べUACにじゃまされそうだったら,Shell.ApplicationのShellExecuteメソッドで自分自信をrunasオプションをつけて呼び出す。というコードを,スクリプトの先頭に書くという方法です。
Shell.Executeの第4引数に"runas"を渡すと,「管理者として実行」したのと同じことになるみたいです。
では,その方法を使って,既存の変更系スクリプトを「管理者として実行」するスクリプトを書いてみました。
Dim objWMI, objShell, osInfo, os Set objWMI = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\.\root\cimv2") Set osInfo = objWMI.ExecQuery("SELECT * FROM Win32_OperatingSystem") flag = false For Each os in osInfo If left(os.Version, 3) >= 6.0 Then flag = true End If Next Set objShell=CreateObject("Shell.Application") If flag Then ' 引数付きでwscriptを管理者権限で再実行 objShell.ShellExecute "wscript.exe", """" & WScript.Arguments(0) & """","","runas",1 Else objShell.ShellExecute "wscript.exe", """" & WScript.Arguments(0) & """" End If
このスクリプトに runas.vbs などと名前をつけてデスクトップなどに保存し,実行したいスクリプトをアイコンの上にドラッグ&ドロップすると,OSを判断して必要であれば,runasをつけて実行します。