June, 2012 - フィールドSEあがりの安納です - Site Home - TechNet Blogs

フィールドSEあがりの安納です

Microsoft Evangelist -- Junichi Anno

June, 2012

  • Microsoft Virtual Academy ポイント加算キャンペーン

    すっかりBLOG投稿をさぼってしまっていまして、すみません。

    そろそろ落ち着くので、再開いたします。もう書きたいことがたまりまくっていて、どこから手を付けたらよいやら....

    さて、既に6400名に登録いただいている Microsoft Virtual Academy ですが、お得なポイント加算キャンペーンを開催しております。

    以下のキャンペーンコードを、Microsoft Virtual Academy のサイトにある「キーワードを登録する」から登録していただくと、??ポイントが加算されます。

    キャンペーンコード : junichia8945

    image
    https://www.microsoftvirtualacademy.com/MyMVA/Dashboard.aspx

    ちりも積もれば山となる!

    6月末までに登録してください!

  • 【PowerShell】PSComputerName プロパティの存在を忘れ���に

    Windows PowerShell を使用してリモートコンピューターに対して処理を行うには、-ComputerName パラメタを使用するのが一般的です。

    例えば、リモートコンピューターのサービス一覧を取得するには、以下のように入力します。

    Get-Service –ComputerName Target01

    結果は以下の通りです。

    Status   Name               DisplayName
    ------   ----               -----------
    Stopped  AeLookupSvc        Application Experience
    Stopped  ALG                Application Layer Gateway Service
    Running  AppHostSvc         Application Host Helper Service
    Running  AppIDSvc           Application Identity


    出力結果にコンピューター名を含めたい場合には、以下のように書きます。

    Get-Service –ComputerName Target01 | Select-Object MachineName,Name,Status

    MachineName                Name                                          Status
    -----------                ----                                          ------
    Target01                AeLookupSvc                                  Stopped
    Target01                ALG                                          Stopped
    Target01                AppHostSvc                                   Running
    Target01                AppIDSvc                                     Running

    では、リモートコンピューターのサービスを停止したい場合はどうでしょう。

    サービスを停止するのは Stop-Service コマンドレットを使用しますが、Stop-Service には –ComputerName パラメタが用意されていません。

    じゃ、どうするか。

    Invoke-Command コマンドレットを使用して、以下のように書きます。以下は、Windows Update サービスを停止しています。

    Invoke-Command –ComputerName TARGET01 { Stop-Service wuauserv }

    ただ、これだと戻り値が無いので、本当に止まったのかどうかがわかりません。そこで、Get-Service wuauserv も一緒に実行します。

    Invoke-Command -ComputerName TARGET01 {Stop-Service wuauserv; Get-Service wuauser}

    Status   Name               DisplayName                            PSComputerName                      
    ------   ----               -----------                            --------------                      
    Stopped  wuauserv           Windows Update                         tfdc02
            

    ここで、PSComputerName に注目してください。

    ためしに、以下のコマンドレットで Get-Service の出力結果に PSComputerName が含まれているか確認してみましょう。

    Get-Service | Get-Member -Name PSComputerName

    何も出力されません。つまり、PSComputerName は Get-Service の出力結果ではないということがわかります。

    実は、PSComputerName は Invoke-Command の出力結果に含まれるプロパティの1つです。

    Invoke-Command はリモートコンピューターに接続する際に PSセッションを作成しますが、このとき PSComputerName には接続先のコンピューター名が格納されます。

    では、MachineName はどうなったのでしょう?

    以下のコマンドレットを入力してみてください。

    Invoke-Command -ComputerName TARGET01 {Start-Service wuauserv; Get-Service wuauser} | Select-Object MachineName,Name,Status

    MachineName                        Name                               Status                           
    -----------                        ----                               ------                           
    .                                  wuauserv                           Stopped

    MachineName には「.(ドット)」が格納されています。勘の良い方ならばおわかりのように、これは「Current Computer」を意味しています。

    Invoke-Command は、コマンドをリモートコンピューターに投げて、リモートで実行した結果を受け取ります。だから MachineName には Current Computer を意味する「.」が格納されているわけです。

    image

    一方で、Get-Service –ComputerName Target01 はといえば、Get-Service コマンドレットが実行されるのはローカルです。MachineName プロパティには Get-Service のターゲットコンピューターが格納されるので、-ComputerName パラメタと同じ値を取得することができます。

    image

    このように、PowerShell リモーティングでは、そのコマンドがどこで実行されるのかを意識しておく必要があります。

    PCComputerName は、リモーティングではとても重要なぱらめたです。ぜひ覚えておいてください。

  • 【PowerShell】-ComputerName に複数のリモートコンピューターを指定するとどうなる?

    Windows PowerShell のコマンドレットには、-ComputerName というパラメタを持つものが多く存在します。

    これは、WMIを使用してリモートコンピューターのクラスに接続して、情報を取得したり処理を行うためのものです。

    -ComputerName には以下のようにして複数のコンピューターを指定することができます。

    Get-Service -ComputerName tfdc01,tfsv01 |Select-Object name,machinename

    Name                                                 MachineName
    ----                                                 -----------
    adfssrv                                              tfsv01
    AdtAgent                                             tfdc01
    AdtAgent                                             tfsv01
    ADWS                                                 tfdc01
    AeLookupSvc                                          tfsv01
    AeLookupSvc                                          tfdc01


    結果は、キーとなる Name の昇順で表示されていることがわかります。

    もしコンピューターごとにまとめて出力するのであれば、以下のように指定すればOkです。

     Get-Service -ComputerName tfdc01,tfsv01 |Sort-Object MachineName |Select-Object MachineName,Name,Status

    さて、ここで疑問です。

    複数のコンピューター名を指定した場合、処理はパラレルで行われるのでしょうか?それとも ForEach のように順番に処理されるのでしょうか?

    ためしに複数のコンピューターに対して以下のコマンドレットを実行してみます。これはリモートコンピューターに接続して3秒後に現在の日時をミリ秒まで取得し表示しています。

    $List = "tfsv01","tfsv02","tfsv03","tfdc01","tfdc02","tfops","tfsql2"

    Invoke-Command -ComputerName $List {Start-Sleep -Seconds 3 ;$d = Get-Date -Format "yyyy/MM/dd hh:mm:ss.ffffff" ; "$Env:ComputerName $d" }

    TFDC01   2012/06/15 12:22:08.706333
    TFOPS    2012/06/15 12:22:08.911051
    TFSV02   2012/06/15 12:22:08.754154
    TFSV03   2012/06/15 12:22:08.789182
    TFSQL2   2012/06/15 12:22:08.875165
    TFDC02   2012/06/15 12:22:09.124391
    TFSV01   2012/06/15 12:22:09.032098

    もしシーケンシャルに実行されるのであれば、それぞれの時刻に3秒の差があるはずですが、どうもそうではないようです。

    また、シーケンシャルであれば、Listの頭から(もしくはお尻から)実行されるのが普通でしょう。

    と考えると、以下のように実行されていることがわかります。

    • 処理の開始は非同期
    • 結果の表示は早い順
    • すべての処理が完了するまで待ち合わせる

    指定したサーバーの順に、シーケンシャルに実行されるわけではない..という点が何とも素敵ですが、逆にこれが面倒を起こすこともありますね。

    シーケンシャルに処理を行いたい場合には、ForEach 等を使用して前の処理が終わるまで次の処理に移動しないように制御する必要があることに注意してください。

  • 【PowerShell】Invole-Command –FilePath パラメタの魔法

    Invoke-Command を使用するとリモートコンピューター上でコマンドレットを実行することができます。このとき、コマンドレット単体ではなく、スクリプトファイル(.ps1)を使用することができます。

    Invoke-Command で .ps1 ファイルを使用する方法は2つあります。

    Invoke-Command -ComputerName SV01 -FilePath .\hogehoge.ps1

    Invoke-Command -ComputerName SV02 {.\hogehoge.ps1}

    前者と後者の違い、わかりますか?

    前者の場合は、スクリプトファイルのパスはローカルコンピューターです。後者の場合は、リモートコンピューター内のパスになります。

    image

    リモートコンピューターにコマンドレットが存在することさえ確認できていれば、スクリプトファイルをいちいち送信しなくてもよいというのは非常に便利なことです。

    (参考)【PowerShell】Hyper-V 系コマンドレットを手元の Windows 7 から実行するには ~ Import-PSSession

    ちなみに、指定した ps1 ファイルに引数を指定したい場合にはどうしたらよいでしょう?

    以下のように、-ArgumentList パラメタを使用して指定します。

    Invoke-Command -ComputerName SV01 -FilePath .\hogehoge.ps1 -ArgumentList “Arg1",“Arg2",“Arg3"

    PowerShell...知れば知るほど奥が深いです。

  • 【PowerShell】ループバック PS セッションとは

    Windows PowerShell 3.0 では、PowerShell のセッションから一旦切断して、別のコンピューターから再度接続する...ということが可能になっています。

    image

    PowerShell Web Accessを使用すれば自宅からの再接続も可能となり、どこからでも仕事がしたいエンジニアにとっては垂涎の機能です。PSWAを経由すればスマートフォンからでも接続できます。すばらしいです。

    image

    ここで、こんな疑問が出てきます。

    普通はサーバーに「ローカルログオン」して作業しているから、
    そこに再接続できるとうれしいんですけど

    はい、可能です。

    それがループバックPSセッションです。

    仕組みも使い方も、リモーティングと全く同じです。

    PowerShell コンソールを開いたら、以下のようにしてローカルコンピューターに対して PS セッションを開設します。

    PS C:\> $Session = New-PSSession

    PS C:\> Enter-PSSession $Session

    [LOCALHOST] PS C:\>

    プロンプトが [LOCALHOST] となったことに注意してください。これで、ローカルコンピューターに PS セッションを経由して入り込んだことになります。

    業が完了したら PowerShell コンソールを閉じる前に以下のコマンドを入力します。

    [LOCALHOST] PS C:\> Exit

    PS C:\> Disconnect-PSSession $Session

    Id Name            ComputerName    State         ConfigurationName        Availability
    -- ----            ------------    -----         -----------------        --------
    1 Session1        localhost       Disconnected  Microsoft.PowerShell     None

    そうすると、これまで行っていた環境は、Session1 という名前で、State=Disconnected(切断中)、Availability=None(誰も使っていない) となり、未使用のセッションとして保存されます。

    あとは、のマシンから以下のように入力して再接続することができます。

    PS C:\> $MySession = Connect-PSSession –ComputerName <サーバー> –Name Session1

    PS C:\> Enter-PSSession $MySession

    [サーバー] PS C:\>

    で、ですね。

    おそらく、こう思うはずなのです。

    めんどくせっ!

    そうなんです。せめて、PowerShell コンソールを開いたら、自動的にループバックセッションが有効になるようにできれば...と思うわけです。

    そこで、この処理をプロファイルに埋め込んでしまう方法を考えてみます。プロファイルに埋め込めば、起動時に自動的にセッションの作成や接続を行ってくれます。

    PowerShell プロファイルの場所は、$PROFILE に格納されていますので、確認してください。おそらく、以下のパスだと思います。

    C:\Users\<ユーザーID>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

    フォルダが無い場合には自分で作り、その下に Microsoft.PowerShell_profile.ps1 ファイルを作成します。テキストファイルです。

    メモ帳か PowerShell ISE で開いたら、以下のように入力してください。

    $LoopbackPSSessioin = New-PSSession
    Enter-PSSession $LoopbackPSSessioin

    function quit ()
    {
       Disconnect-PSSession $LoopbackPSSessioin
       exit
    }

    何やってるかわかりますか?

    1行目と2行目は、コンソールの起動時に実行されます。つまり、ローカルに PSセッションを張って、そこに乗り込んでいるわけですね。コンソールを起動すると、自動的に以下のような表示になります。

    [localhost]: PS C:\Users\administrator.TF\Documents>

    そして閉じるときは....

    [localhost]: PS C:\> Exit  ← これは PS セッションから抜けるため

    C:\PS > quit  ← $LoopbackPSSession を残すためにこのコマンドで抜ける

    と入力します。 Quit コマンドにより、Disconnect-PSSession $LoopbackPSSessioin が実行され、セッションが正常に切断されてコンソールが閉じます。

    コンソールを右上の[×]で閉じてしまったり、Exit コマンドで閉じてしまった場合は残念ながら保存されません。

    ・・・・・・・・・・・・・・・・・・・

    うーん、いまいちだなぁ...と金曜日の夜に物思いにふけるのでした...。

  • 【PowerShell】リモートコンピューターが再起動するまで待ち合わせるには

    Windows PowerShell には Restart-Computer という便利なコマンドレットが用意されています。

    Restart-Computer –ComputerName TARGETCOMPUTER とすれば、リモートコンピューターを再起動することができます。

    もしリモートコンピューターに誰かがログオンしていても、-Force を指定することで強制的に再起動できます。もちろん管理者権限が必須です。

    さて、ここで問題です。

    リモートコンピューターに対して複雑な処理をしているとき、一度再起動が必要な処理があったとします。

    もちろん、再起動後にも処理は継続しなければなりません。

    どうしましょう?

    例えば、以下のようなスクリプトを作れば、待ち合わせは可能です。

    ※ 2012.6.18 14:21 スクリプトに不備があったので修正しました...すみません

    New-PSSession -ComputerName $ServerName
    Restart-Computer -ComputerName $ServerName -Force
    do #セッションが切断されるまで待ち合わせ
    {
        Start-Sleep -Seconds 1
    } Until ((Get-PSSession -ComputerName $ServerName).count -eq 0)
    do #セッションが再接続されるまで待ち合わせ
    {
        Start-Sleep -Seconds 1
        $S = New-PSSession -ComputerName $ServerName
    } Until ((Get-PSSession -ComputerName $ServerName).count -ne 0)
    echo 再接続されました $S
    Enter-PSSession $S

    比較的簡単に待ち合わせができることに、PowerShell 初心者の方は驚くかもしれませんが、まだ驚くのは早いです。

    PowerShell 3.0 では、たった1行で待ち合わせが可能です。

    Restart-Computer –ComputerName SERVERNAME –wait

    これだけです。

    すばらしすぎて腰が抜けそうです。

  • 【PowerShell】PowerShell 3.0 を使用した Windows Server 2012 管理の自動化

    2012年6月は、毎週1回、10:00~11:30に Windows PowerShell 3.0 のセミナーを継続的に開催しています。

    これまでに、2回開催し、残すところあと2回となりました。

    • 基礎編(終了)
    • リモーティング編(終了)
    • ワークフロー編(6月19日)
    • 開発者編(PowerShell コマンドレットを使って管理アプリを作る)(6月25日)

    既に満席となっておりお申込みいただくことはできないのですが、ひとまずワークフロー編までの資料を以下に公開しました。

    随時アップデートしていく予定ですので、興味のある方はダウンロードしてお使いください。

  • 【PowerShell】長時間バッチ処理中に停電でサーバーがシャットダウン!でも Workflow ならば安心です

    Windows PowerShell 3.0 にはワークフロー機能が実装されています。もちろん、そのベースとなっているのは Windows Workflow Foundation 4.0 です。

    例えば、以下のようなワークフローがあったとします。このワークフローでは、UserList.csv ファイルにの保存されたユーザー一覧を読みこんで、大量のユーザーを順次作成する処理を想定していると考えてください。

    Workflow CreateUser
    {
        Get-Content -Path \\junichia-vdi\tools\ps\wf\UserList.csv -Encoding String | `
                Out-File -Path .\UserList_Unicode.csv -Encoding unicode

        $UserList = Import-Csv -Path .\UserList_Unicode.csv

        foreach  ($u in $UserList)
        {
            $UserID = $u.userID
            Echo -InputObject "$(Get-Date) $UserID を作成します"
            $password = convertto-securestring -Input $u.initialpassword -asplaintext -force
            $Department = $u.Department
            $FirstName = $u.FirstName
            $LastName = $u.LastName
            $HomeDirectory = \\Home\Share\$userID

            InlineScript
            {
                New-ADUser `
                   -Name $using:UserID `
                   -AccountExpirationDate 2012/12/31 `
                   -AccountPassword $using:password `
                   -ChangePasswordAtLogon $using:true `
                   -Department $using:Department `
                   -GivenName $using:FirstName `
                   -Surname $using:LastName `
                   -HomeDirectory $using:HomeDirectory `
                   -HomeDrive "Z:" `
                   -ErrorAction SilentlyContinue `
                   -ErrorVariable ERRORMESSAGE

                If ($ERRORMESSAGE.Count -eq 0)
                {
                  Echo -InputObject "$(Get-Date),$using:UserID を作成しました"
                } else {
                  Echo -InputObject "$(Get-Date),$using:UserID $ERRORMESSAGE"
                }
            }

            Checkpoint-Workflow

        }
    }

    ワークフローの実行までの流れは以下の通りです。

    image

    この例では、Invoke-Command を使用してリモートのドメインコントローラー上で、ワークフローをバックグラウンドジョブとして実行しています。

    上図の左側に「Stopped」や「Running」と書かれているのはワークフローの状態です。

    処理も半ばに差し掛かったころ、なんと停電の影響によりUPSからの指示でサーバーがシャットダウンを始めてしまいました。

    このとき、ワークフローは自動的に「Syspended」という状態に移行します。

    つまり、実行中のコマンドレットを、その状態ごとフリーズし保存してくれます。

    ワークフロー内部で扱っていた変数や出力結果もそのまま残されています。

    復電してドメインコントローラーが起動すると、ワークフローは自分ではレジュームできません。

    管理者が再度ドメインコントローラーに接続して Resume-Job コマンドレットによりジョブを再起動してあげる必要があります。

    通常、ジョブはサスペンドする直後からの処理を再開するので、ユーザー登録は漏れ無く実行することができます。

    もし、UPS管理ソフトがコマンドを発行できるタイプであれば、シャットダウン前にジョブをサスペンドし、次回起動時に自動的にレジュームするようスケジューリングすることもできます。

    VBSを使用していた時代は、サーバーが落ちたりクライアントが落ちると、ログを解析して続きのバッチを組みなおさなければなりませんでしたが、PowerShell 3.0 ではワークフローによって不測の事態の復旧が、とても簡単になりました。