Windows管理

Windowsを管理してゆく上でのTipsやスクリプトなどを紹介します。

Robocopyでフォルダをコピー

あるフォルダを別なフォルダにミラーリングコピーする方法としては,以前は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オプションを記述しておきます。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

ファイルサーバ入替に向けて

先週末から、いろいろ悩みすぎました。 解決したと同時に疲れがドドドドド。 疲れがピークなのか、カフェオレをこぼして書類が茶色(トホホ 加えて、頭痛までしてきたんですけど! 来週あたりに、ファイルサーバの入れ替えがあるので、 その前にと、ファイルコピー:時間測定とアクセス権(ACL)設定のテストを行ってました。 最初xcopyでやってたんですが、 なぜかアクセス権のコピーがうまくいかないんですよ。/oスイッチ効かないし。 /xスイッチ使うと「クライアントは要求された特権...
添付サイズ
Robocopy.docの日本語版367.5 KB

Robocopy よく使うオプション

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にコピーする

NAS製品の中では,タイムスタンプの精度がFATなみの2秒というものがあります。そのくせNTFSを名乗っている。そんなディスクとNTFSのディスクの間でミラーリングをした場合に,タイムスタンプの精度の違いにより,毎回コピーされてしまったり全然コピーされなかったりとうまく動かないことがあります。そんなことが起きないように,/FFT オプションを使います。

robocopy C:\userdata \\server\userdata /MIR /FFT

/FFTでは,タイムスタンプの精度が2秒であるとしてコピーをします。

コピーではなく移動する

コピーではなくフォルダ構造全体を移動したい場合は,/MOVE オプションを使います。

robocopy C:\userdata \\server\userdata /MOVE

よく似た感じの /MOV オプションもあります。こちらは,コピー元に(空っぽの)フォルダ構造が残ります。/MOVE ではフォルダも含めて元のパスにはなにもなくなります。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Active Directoryのユーザ管理 (1)

このトピックはExcelのVBAを利用してActive Directoryのユーザ情報を管理するやり方についての記録です。

Excel VBAの参照設定

Active DS Type Library
Microsoft Scription Runtime
Microsoft WMI cripting V1.2 Library

上記を追加する

Active Directoryユーザ管理のためのモジュール

mdlADSI

後の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 

 

 

CADSI.bas

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のユーザ管理をするコードを書いてゆく。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Active Directoryのユーザ管理 (2)

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

ユーザをドメインから削除した後に,ホームディレクトリを削除しているだけ。

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

ログオン時にドライブやプリンタを割り当てる

ここで紹介するlogon.vbsは,共有フォルダをドライブ名に割り当てる作業や,ネットワークプリンタに接続する作業をログイン時に自動的に行うことを主な目的とするスクリプトです。ユーザの所属するグループによってネットワークドライブの割り当てを変更したり,コンピュータによって接続するネットワークプリンタ や通常使うプリンタを変更したりできるようになっています。

  1. グループによるネットワークドライブの割り当てを自動的にできるので,各ユーザに設定作業をしてもらう必要がない。
  2. プリンタの割り当てを自動ですることにより,新しいコンピュータが入ってもいちいちプリンタのインストール作業をする必要がない。また新しいプリ ンタが入った場合でもサーバにプリンタをインストールし設定ファイルに記述を追加するだけでよく,各コンピュータへのインストール作業をする必要がない。

これらの機能でクライアントコンピュータの初期設定や設定変更が格段に楽になります。

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. //(二つの連続したスラッシュ)で始まる行はコメントとして無視されます。
  3. 空行(改行のみからなる行)は無視されます。

対象オブジェクト

対象オブジェクトとは,そのコマンドを実行する条件指定のようなものです。スクリプトを実行しているユーザやコンピュータが所属するグループやコン ピュータ名によってコマンドの実行を制御することができます。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アドレスを取り出すのにやや時間がかかるので,処理速度に影響を与えます。

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では,ネットワークドライブ接続のコマンドがあった場合は次のような動作をします。

  1. 指定されたドライブ名にすでに割り当てられているネットワークパスがない場合は,割り当てを実行します。
  2. 指定されたドライブ名にすでに割り当てられているネットワークパスがあり,それが指定されたパスと異なる場合は,以前の割り当てを解除してから,新しいパスを割り当てます。
  3. 指定されたドライブ名にすでに割り当てられているネットワークパスがあり,それが指定されたパスと一致する場合は,なにもする必要がないのでなにもしません。

すでに接続されているネットワークドライブを切断するだけの場合には,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して分割する利点はいくつかあります。

  1. マップファイルの記述を短くできます。複雑で長くなったマップファイルを分割して簡潔なマップファイルの組み合わせにできます。
  2. 動作速度を向上させます。logon.vbsはexitコマンドに出会わない限りマップファイルを最後まで読んで処理を実行します。マップファイルを分割することで不必要な行を最初から読み込ませないように工夫することで処理速度を向上させることができます。
  3. 複 雑な条件による動作を可能にします。マップファイルでは1列目と2列目で指定した条件の場合に3列目以降のコマンドが実行される仕様です が,○○かつ △△というような条件指定はできません。このような場合には○○の場合にマップファイルをincludeするように記述し,△△の部分はマップファイルに 書くことによって実現できます。

ダウンロード

logon.vbsのダウンロードはこちら

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。
添付サイズ
logon.vbs15.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を指定します。この例ではファイル名の指定で月ごとにファイルが変わるようにして あります。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

ログオンログオフのイベント送出スクリプト

ドメインに参加しているコンピュータにログオン・ログオフしたときに、イベントログにログを残す。いろいろな記事をつぎはぎして作成しました。各記事の作成者様、ありがとうござい...

ホスト名を変更するスクリプト

Active Directory のドメインに参加しているPCのホスト名をある理由から変更しなければならなくなった。それも数十台。現場に行って作業をするのは面倒なので,スクリプトを書くことにしました。

ホスト名の変更は,Win32_ComputerSystem クラスの rename メソッドで変更できます。このメソッドを実行するには,ローカルPCに対して管理者権限を持っていなければなりません。今回の対応先では,Domain Users がローカルのAdministratorsグループに入っているので,その点は大丈夫です。ですがドメインに入っているPCのホスト名を変更するのですから,Domain Admins の権限が必要になります。ユーザーに管理者のユーザー情報を教えるわけに行きませんので,それはスクリプトに埋め込みたいところです。rename メソッドにドメインの管理者のユーザー情報を渡してやれますので,それを利用します。

ホスト名対応表にDictionary

古いホスト名を新しいホスト名にするわけですから,その対応表が必要になります。ここでは,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
同じユーザーによる、サーバーまたは共有リソースへの複数のユーザー名での複数の接続は許可されません。サーバーまたは共有リソースへの以前の接続をすべて切断してから、再試行してください。

というエラーで,なぜにこれがでたのかよくわかりません。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Winsを設定するスクリプト

職場の環境で,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情報を表示させています。

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

プリンタの共有名を得る

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

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Windowsサーバでのレプリケート失敗を解決

運用しているサーバで,Active Directoryのドメインサーバ間でのレプリケートができなくなっていた。しょうがないので,NETLOGONに保存されているLogonスクリプト に変更があった場合には,二つのサーバのファイルを更新していた。しかしそれではめんどうだし,エラーが発生しているまま放置しているのも気持ちが悪いの で,本気で直すことにした。

出入りのメンテナンス業者のエンジニアに聞いたところ,サーバを設置した後,ネットワーク機器の接続を変更した りした場合にそのようなことが起きることがあるという。サーバ間のレプリケートの接続設定は,2台目のドメインサーバが追加されたときに自動設定される。 そのごネットワーク機器の変更があると,相手先のサーバが見つからない状態になってしまうらしい。まずはその対策を施すことにした。

  1. Active Directoryサイトとサービスを起動
  2. Site/Default-First-Site/Servers/Server1を展開
  3. リストビューペインでNTDS Settingsを右クリック
  4. 「新規作成」「接続」を実行。
  5. レプリケート先にServer2を指定する。
  6. 接続先が2つになるので,元々あった自動設定の接続を削除する。
  7. これらの作業をServer2からServer1への設定でも繰り返す。

これはつまり,自動設定だった接続の設定を手動で設定し直してやるという作業。ここまで作業して帰宅し,翌日確認したが,レプリケートは失敗していた。

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 値] で追加することが できます。
上記の値名を正しく入力してください。

おっしゃるとおりにやってみることにした。

  1. regeditでEnable Journal Wrap Automatic Restoreを追加し,その値を1にした。
  2. Dosプロンプトから,
    net stop ntfrs
    net start ntfrs
    を実行してntfrsサービスを再起動した。
  3. Enable Journal Wrap Automatic Restoreの値を0にした。

そうすると,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にて次の作業を行った。

 

  1. Active Directoryサイトとサービスを起動
  2. Site/Default-First-Site/Servers/Server1/NTDS Settingsを展開
  3. リストビューペインでServer2を右クリック「今すぐレプリケート」を実行。

NETLOGON共有で確かめてみると,同期がとれていた。その後,レプリケートのエラーは発生していない。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Windowsサーバを別名で運用する場合の問題

とあるサーバ,[sv1]のファイル共有を,とある理由で別名[alias1]というサーバ名で接続したい。これは簡単だと思った。DNSにCNAMEレコード[alias1]を作って,ターゲットホストを[sv1.domain]とすればいいと思った。しかし・・・

エクスプローラのアドレス欄に[\\alias1]と入力してみると,「alias1が見つかりません。」とのつれないお言葉・・・

それでは,と,ファイル名を指定して実行から[\\alias1]を入力してみると

ネットワークに重複した名前があるため接続されませんでした。コントロールパネルのシステムでコンピュータ名を変更してから再実行してください。

という意味不明(だって重複してないし・・・)なメッセージが出てサーバの共有を表示できません。

解決法

これを解決するには,公開しているファイルサーバ上でレジストリに値を設定する必要がありました。

  1. sv1サーバにログインしてregeditを起動します。
  2. 以下のレジストリ キーを探します。
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\lanmanserver\parameters
  3. 次のDWORD値を追加します。
    値の名前=DisableStrictNameChecking     値=1
  4. サーバを再起動します。

以上で,[alias1]でファイル共有にアクセスしても接続できるようになります。

参考

Windows 2000 ベースのサーバー上の SMB 共有へのエイリアス名による接続が機能しない

上記のページは「Windows 2000の」となっているが,原文ページでは

Windows 2000-based computer or a Windows Server 2003-based computer

となっているので,2003も対象となります。2003の場合上記文書の「修正プログラム」の適用は必要ありません。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

スクリーンセーバーとパスワードの強要

セキュリティを確保するために,Windowsクライアントをログインした状態のまま一定時間操作がない場合にロックさせたい。こういった要望は普通にあることだと思います。

ローカルPCでこれをするには,コントロールパネル > 画面 > スクリーンセーバー にて,

  1. スクリーンセーバーと待ち時間を設定し,
  2. パスワードによる保護のチェックを入れる

で,PCがロックされるようになりますが,ちょっと知っている人は,「うっとおしい」と言ってこの設定を外してしまいます。これをさけるためグループポリシーで強制し,ユーザーが変更できないようにしたいと思います。

  1. グループポリシーオブジェクトエディタを開き,左側のツリーからユーザーの構成 > 管理用テンプレート > コントロールパネル > 画面 を選択します。
  2. 右側ペインで,「スクリーン セーバーを使用する」を「有効」にします。
  3. 同様に「スクリーン セーバーパスワードで保護する」を「有効」にします。
  4. スクリーン セーバーのタイムアウトを「有効」にし,タイムアウトまでの秒数を設定します。

「スクリーン セーバーの実行可能ファイル名」も指定すべしということも聞きます。これは,パスワードによる保護に対応していないスクリーンセーバーがあるからそのように記述しているのでしょうか。私が管理しているところ場合は,お気に入りのスクリーン セーバーを使っている方が多いので,この設定は「未構成」のままにしています。

 

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Active Directoryで特定ユーザーのログオンを拒否する

Active Directory を他のWebサービスのLDAP認証サーバーとしても利用しているような場合。Webサービスは使ってもらいたいが,パソコンにログオンして欲しくはないユーザーというのがあると思います。そういう人をログオンさせないようにするには,どうしたらよいでしょうか。

グループを作る

ログオンさせたくないユーザーはだれなのかを判断しなければなりませんから,まずはグループを作ります。ここでは,冒頭の例に沿ってldapusersというグループを作ります。そして,パソコンにログオンさせたくないユーザーをこのグループに所属させます。

ローカルでログオンを拒否する

グループポリシーに「ローカルでログオンを拒否する」というポリシーがあります。このポリシーにグループを設定します。

2008 Server の場合

  1. グループポリシー管理エディタの左ペインで,
    コンピュータの構成 > ポリシー > Windowsの設定 > セキュリティの設定 > ユーザー権利の割当
    を選択します。

     
  2. 右ペインの,「ローカルでログオンを拒否する」をダブルクリックします。
  3. 「これらのポリシーの設定を定義する」のチェックを入れて,「ユーザーまたはグループの追加」をクリックします。

     
  4. 表示されたダイアログに先ほど設定したグループを(ドメイン名\グループ名の形式で)設定します。「参照」ボタンでADオブジェクトの中から選択もできます。

2003 Server の場合

  1. グループポリシーオブジェクトエディタで,コンピュータの構成 > Windowsの設定 > セキュリティの設定 > ユーザー権利の割当 を選択します。
  2. (以降は2008 Serverと同じです)

 

 

 

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Server 2008 でwpadが参照できない

別エントリで,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が引けるようになりました。グローバル クエリ禁止リスト自体を無効にすることもできるようです。

参考サイト

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

USBメモリにAutorun.infフォルダを作成

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フォルダを作成します。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

WebClientサービスをポリシーで止める

サーバー上にあるファイルをエクスプローラーで右クリックするとなんか遅い,20秒程度待たされる,という現象が発生しました。あるサーバーではOKだけど,あるサーバーでは発生します。原因は何なのかググってみると,WebClientが原因のようでした。

実は,以前に他の問題でWebClientが原因であると言うことがありました。ログオンに時間がかかるというものです。

Windows XP を実行するコンピュータでドメインにログオンするとき、またはネットワーク リソースに接続するときに、時間がかかる

そのときは,ドメインコントローラーにてIISをたてて,Port80へのアクセスにとりあえず返事をさせるという方法で解決をしました。WebClientがWebDAVでの接続を試みてもIISでPort80に対してすぐにレスポンスしてやればいい,という発想です。各クライアントのサービスを停止するのもなぁ,と思いましたので。

その後,我が職場のサーバーも数が増えて,別のサーバーではIISも立ち上げておらず,WebClientがPort80のタイムアウトを待ってから接続するため,いろんなサーバーの最初の接続が遅くなっていたわけです。これは何か根本的な対策が必要だと言うことになりました。

WebClientサービスをポリシーで止めることにしようとググってみると,「犯人はWebClientサービス?」というページが見つかりました。これです。全く同じ現象。こちらのサイトでは,ログオン スクリプトでサービスを停止するという方法をとっておられましたが,僕はポリシーでやってみることにしました。

Server 2003 にて設定

グループ ポリシー オブジェクト エディタ の左ペインのツリーで,

コンピュータの構成 > Windowsの設定 > セキュリティの設定 > システムサービス

を選択します。右ペインにサービス名がずらっと並びますので,WebClientをダブルクリックして,

「このポリシーの設定を定義する」にチェックを入れて「手動」を選択します。

これでドメインに参加している各クライアントに対してWebClientサービスを停止することができました。

追記:2008 Server の場合

同じことを,今度は2008 Serverがドメインコントローラーになっているドメインでやろうとしたところ,問題が発生しました。グループ ポリシー管理エディタの上記の場所にWebClientが存在しないのです。

これは2008 Server自体にWebClientサービスがないからなんだそうです。でもドメインのクライアントはほとんどがXpで,それらのWebClientサービスを停止したいのです。しかし一覧に存在しないことには,それを「無効」に設定することができません。どうしたものかと思っていたら次の記事を見つけました。

VistaのWebClientサービスをWin2k8のグループポリシーを利用して無効化 ...

この中に

グループポリシーの作成はVistaで行い、確認をWindows Server 2008で行いました。

という記述がありました。クライアントでグループ ポリシー オブジェクト エディタを実行すれば,クライアントにあるサービスは表示されるということのようです。私のクライアント(OSはXp)にはWindows Server 2003のadminpakがインストール済みでしたので,ローカルにインストールされている「Active Directory ユーザーとコンピュータ」を起動し,対象のドメインに接続して,グループポリシーの編集まで行ってみると,そこにはWebClientポリシーが表示されています。表示されているのでとりあえずそれを無効に設定してみました。これで本当にポリシーが設定できたのでしょうか。もう一度サーバー上でサーバー マネージャを起動してポリシーを確認してみると,

ちゃんとWebClientの項目が追加され,「無効」になっています。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

Windows 7 でXP Modeを試す

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を作り直しですか?こりゃたまりません。

うちの勤務先での結論は「こりゃ,使えねぇ。別な方法考えなきゃな」に固まりつつあります。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

ドメインコントローラで裏LANを使う

サーバー機には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を起動し,サーバ名を右クリックしてポップアップ・メニューの[プロパティ]を実行すると,次のようなダイアログが表示されます。[詳細]タブにある[サーバ オプション]でラウンドロビン機能の有効/無効を設定することができます。

裏LANのアドレスを隠す

それでも,DNSでserver01を引いたときには,裏LANのIPも返ってきます。裏LANのIPをDNSが返さないようにするには,同じダイアログの「インターフェイス」タブで設定します。

デフォルトではリッスン対象が上図のように「すべてのIPアドレス」になっています。これを「指定したIPアドレス」に変更して,裏LANのIPアドレスを削除します。

上記のようにするとDNSには裏LANのNICは登録されなくなります。この処理をする場合は,前項のラウンド ロビンの無効化は設定する必要はありません。

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

ドメインコントローラーの共有フォルダにMacからアクセスできない

Windowsサーバーの共有フォルダにMacのOSXから比較的簡単にアクセスできますが,共有フォルダがドメインコントローラー上にあるとうまく接続できません。同じドメインに所属するサーバーでも単なるメンバサーバーであれば接続できるのに,ドメインコントローラーの場合は正しい認証情報を入力しても接続エラーが発生します。

これは,「Macintosh の SMB 接続は "SMB 署名" というものに対応していないらしく、そしてドメインコントローラではこれがデフォルトでは必須となるそうで、そのせいで認証に失敗してしまうということでした。」というのが原因だそうです。

そこで,参考サイトの記事の通り,ドメインコントローラーのセキュリティポリシーを変更することにしました。

コンピュータの構成 > Windowsの設定 > ローカルポリシー > セキュリティオプション

にある,「Microsoft ネットワーク サーバー: 常に通信にデジタル署名を行う」が「有効」になっているので,これを「無効」に変更します。

ポリシーをすぐさま反映させるために,gpupdateを実行します。

C:\>gpupdate

以上の操作をすることでOSXからもファイル共有に接続できるようになりました。

ずーっと以前Xerox製の複合機からスキャンする際,サーバー上のフォルダへプッシュする設定を試したときにも,メンバサーバー上のフォルダにはプッシュできるのに,ドメインコントローラーだとだめだったことがありました。今考えてみるとあれも,この件が原因だったのではないかと思います。

参考サイト

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

プロキシ設定をクリアするスクリプト

 私の職場ではインターネットへ出るのに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

 

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。

設定系スクリプト実行時にUACに邪魔されるのを回避する

 VBSで設定系のスクリプトをWindows 7などで実行すると,UACにひっかかって Access Denied あたりのエラーがでちゃってうまく動作しません。

これを回避する方法をいろいろ調べたのですが。次のサイトなどにあった情報で解決できました。

VBScriptでVistaのUACの権限昇格を行う方法

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をつけて実行します。

トラックバック


URL から "-MoIyadayo" を削除してトラックバックを送信してください。
トラックバックは承認後に表示されます。