2015/06/14

わんくま同盟大阪勉強会#63でのセッション資料を公開します。

デモで使用したサンプルスクリプトも併せてご利用ください。

わんくま同盟の勉強会は今回の大阪#63で10年目に入ったのですが、実は私も9年前の大阪#4で勉強会セッションデビューをしていたりします。

さて、今回はPowerShell DSCリソースを作成するというテーマで話しました。DSCでは管理対象ごとにロジック(リソース)と設定(Configuration)を分離でき、一貫性を持ったインフラ構成の自動化が可能になる素敵な機能です。が、いかんせん、ビルトインリソース(OS標準で含まれるリソース)の種類が少ないので、実際の業務で使うにはカスタムリソースを作成する必要が出てくると思います。

今回のデモでは、テキストファイルの中身を自動構成するという、ごく単純なサンプルを作成して実演してみましたが、考え方や実装方法の基本はこれでカバーできるのではないかと思います。

より詳しくは、ぎたぱそ氏の記事を読んでいただければ良いかと思います。本番で使えるPowerShell DSCリソース作成入門 - Build Insider

サンプルスクリプトの説明
  1. 事前準備

    今回のデモはWin10 Insider Preview(PowerShell 5.0)上で行いましたが、PowerShell 4.0環境でもおそらく動作すると思います。なお、今回のConfigurationはローカルコンピュータに対しPush適用(Start-DscConfigurationコマンドレットによる手動適用)することを想定しています。あらかじめ、DSCが実行可能な環境(スクリプト実行ポリシー、PSリモーティング、Local Configuration Managerの設定等)を整えておいてください。また、スクリプトはすべて管理者権限で実行してください。

  2. xDSCResourceDesignerのインストール

    DSCリソースのひな形を作成するためのxDSCResourceDesignerをインストールします。v5環境であればPowerShellGetを用い、Install-Module xDSCResourceDesigner で入ります。v4環境の場合はTechnetからDSC Resource KitをDLしてください。

  3. xDSCResourceDesignerの使用方法の確認

    xDSCResourceDesignerの使用方法を確認します。make_Foo_resource_template.ps1を実行すると、xDSCResourceDesignerを用いてDSCリソースFooのひな形が$env:ProgramFiles\WindowsPowerShell\Modules\TestResourceに作成されます。ひな形がどのように作成されるかを確認してください。また、Get-DscResource -Name Fooとして、DSCリソースがきちんと認識されているか、確認してください。

  4. TextFileLineリソースの作成

    今回のデモで作成したTextFileLineリソースは、make_TextFileLine_resource_template.ps1を実行してまずひな形を作成しました。作成されたひな形を用いて、TextFileLine.psm1に実際のロジックを記述し、DSCリソースを完成させました。zipに含まれるTestResourceフォルダの中身が、今回作成したDSCリソースモジュールになりますので、まずは内容を確認してみてください。特に、Set-TargetResource関数はどのように実装すると冪等性を保持して作成できるのかを念頭に置いてみてください。

  5. TextFileLineリソースの展開

    zipに含まれるTestResourceフォルダを$env:ProgramFiles\WindowsPowerShell\Modules\の下にコピーしてください。

    Get-DscResource -Name TextFileLineとしてDSCリソースが認識されていることを確認してください。

  6. TextFileLineリソースを用いたConfigurationの作成

    start_dsc_configuration.ps1に含まれる、TextFileLineTestが、今回適用してみるConfigurationになります。

    start_dsc_configuration.ps1を実行すると、ドキュメントフォルダにmofファイルを生成し、Start-DscConfigurationコマンドレットにより設定を反映させます。

    正しくConfigurationが適用されれば、ドキュメントフォルダにlist.txtというファイルが生成し、中にプログラム言語のリストが記入されているはずです。

  7. Configurationが反映されたことを確認

    Test-DscConfigurationコマンドレットを実行すると、現在の状態とConfigurationに書かれた状態が一致していればTrueと表示されます。今はConfigurationを適用した直後なのでTrueになるはずです。

    またGet-DscConfigurationコマンドレットを実行すると、現在の各プロパティの状態を表示してくれます。

    list.txtに含まれる行をテキストエディタで編集して上書きしたりすると、Test-DscConfigurationの結果はFalseになるはずです。

    list.txtを手動で変更した状態で、再度start_dsc_configuration.ps1を実行すると、再びConfiguration通りの状態に戻ると思います。その際、変更のなかったプロパティに関しては、処理がスキップされていることをログをみて確認してください。

  8. その他

    Configurationを色々書き換えて試してください。例えばEnsure="Absent"にすると対象項目が存在しない状態になります。

2014/09/09

8/23わんくま横浜勉強会で、PowerShellコマンドの書き方というセッションをしたのですが、その際、株式会社Codeerさんが公開されているFriendlyというライブラリを使ったコマンドレットを動作させるデモを行いました。

準備の時間がなくて、突貫工事で作ったサンプルで恐縮ですが、公開することにします。(一応動かしてみたら動いた、レベルのものなのであしからず…)

コード
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Dynamic;
using System.Windows.Forms;

namespace Winscript
{
    [Cmdlet(VerbsCommon.Get, "FormControlText")]
    public class GetFormControlTextCommand : Cmdlet
    {
        [Parameter(Mandatory = false, ValueFromPipeline = false, Position = 1)]
        public string[] ControlName { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public Process Process { get; set; }

        protected override void ProcessRecord()
        {
            var _app = new WindowsAppFriend(this.Process);
            dynamic form = _app.Type<Control>().FromHandle(this.Process.MainWindowHandle);
            foreach (var c in form.Controls)
            {
                if (ControlName == null || ControlName.Contains((string)c.Name))
                {
                    WriteObject((string)c.Text);
                }
            }
        }
    }

    [Cmdlet(VerbsCommon.Set, "FormControlText")]
    public class SetFormControlTextCommand : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = false, Position = 1)]
        public string ControlName { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = false, Position = 2)]
        public string Text { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public Process Process { get; set; }

        protected override void ProcessRecord()
        {
            var _app = new WindowsAppFriend(this.Process);
            dynamic form = _app.Type<Control>().FromHandle(this.Process.MainWindowHandle);
            foreach (var c in form.Controls)
            {
                if (ControlName == (string)c.Name)
                {
                    c.Text = Text;
                }
            }
        }
    }
}
ビルド方法
  1. Windows 8.1 SDK をインストールする。
  2. Visual Studio 2010以降でC#のクラスライブラリを新規作成する。
  3. C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0 にある.dllを参照設定する。
  4. 対象フレームワークを.NET 4.5、対象プラットフォームをx64にする。
  5. 上記のコードを貼り付ける。
  6. NuGetでFriendlyおよびFriendly.Windowsを追加する。
  7. ビルドする。
使用方法

上記をビルドして生成したDLLにはGet-FormControlTextと、Set-FormControlTextの2つのコマンドレットが含まれます。

# コマンドレットのインポート
Import-Module "dllのフルパス"

# 操作対象のプロセスオブジェクト取得
$p = Get-Process WindowsFormsApplication1 

# プロセスを指定して、すべてのコントロールのテキストを取得
Get-FormControlText -Process $p

# プロセスとコントロール名を指定して、テキストを取得
Get-FormControlText -Process $p -ControlName textBox1

# 位置パラメータなので以下のようにも書ける
Get-FormControlText $p textBox1

# パイプラインからプロセスオブジェクトを入力することもできる
$p | Get-FormControlText -ControlName textBox1 

# プロセスとコントロール名を指定して、テキストを変更する
Set-FormControlText -Process $p -ControlName textBox1 -Text Wankuma
Set-FormControlText $p textBox1 Yokohama
$p | Set-FormControlText -ControlName textBox1 -Text 6
制限事項

フォーム直下に配置されたコントロールしか取得できない(と思います)。

Windows Formアプリケーションにしか対応していない(と思います)。

x64アプリしか操作できない(と思います。x86用はアセンブリを分ける必要がある??)。(←9/9追記)

コントロール名指定はCase sensitiveでワイルドカード不可です。(この辺ただの手抜きですが)

今後の方針?

上のコードをみてもらえればわかると思いますが、ちょっと触ってみたら一応動くものができるくらい、Friendlyは分かりやすいです。皆さんもぜひ使ってみてください。

本来は、テキストじゃなくてコントロールそのものをGetしたりSetしたりInvoke(クリックとか)したりできるようにしたかったんですが、Controlはシリアル化できないオブジェクトなので、Friendlyで実物を持ってきてコマンドレットに出力する、というのは無理でした。何らかのプロキシオブジェクトみたいなのでラップしたりすれば良いかと思います。

それにしてもPowerShellとFriendlyの組み合わせはものすごい可能性を秘めている予感がします。

システム管理方面では…

セッションでもやりましたが、PowerShellコマンドレットベースのアプリケーションを作ると、GUIとCUIのいいところどりが出来る、とはいうものの、コマンドインターフェースがなくGUIオンリーのアプリケーションはまだまだたくさんあるのが現実かと思います。そういうアプリケーションも、PowerShellデフォルトの機能のみではつらいですが、上記のようにFriendlyを併用すれば、GUI操作の自動化が容易になると思います。

開発方面では…

C#でFriendlyを使ったGUIのテストコードを書く以外に、上記のようなFriendlyの機能をラップしたコマンドレットを用意することで、PowerShellスクリプトでもテストコードを書くことができるようになると思います。その際、Pester等のPowerShell用テストフレームワークを併用すると、より一貫したテストコードが記述できるようになるんじゃないかと思います。

いずれは(いつ?)、Friendlyの機能をラップしたコマンドレット群をきちんと設計、実装して、モジュールとして公開したいですね!

2011/12/19

はじめに

PowerShell Advent Calendar 2011の19日目の記事、そしてこれが私の記事では3回目となります。今回も前々回前回からの引き続きでバックグラウンドジョブについての話題です。前回までは現行バージョンであるPowerShell 2.0におけるバックグラウンドジョブの機能の使い方を解説してきましたが、今回はPowerShellの次期バージョンである3.0に追加される予定の機能のうち、ジョブ関係のものをピックアップしてみます。現在PowerShell 3.0を含むWindows Management Framework(WMF)3.0のCTP2が公開されています。またWindows 8 Developer Preview / Windows Server 8 Developer PreviewにはWMF3.0 CTP1相当のPowerShell 3.0が含まれています。

注意:本記事で取り上げた内容は製品のプレビュー版をもとに記述しています。そのためリリース版では内容が一致しない可能性があることをご承知おきください。

using:ラベル

前回、ジョブに値を渡す方法について解説しましたが、-argumentListに引数として渡すというのは正直めんどうです。呼び出し元のグローバル変数を直接ジョブ側から参照したいですよね。そこでPowerShell v3では新たに変数に付けるusing:ラベルというのが追加されました。このラベルをジョブのスクリプトブロック内で使うと、呼び出し元の変数を参照することができます。具体例。

$test="PowerShell 3.0"
Start-Job {$using:test}|Wait-Job|Receive-Job

とすると、「PowerShell 3.0」と表示され、たしかにジョブのスクリプトブロックから呼び出し元の変数を参照できていることがわかります。これは便利ですね。ただし残念ながらこの方法を使ってもスクリプトブロックをジョブに渡すことはできないようです。相変わらず文字列にキャストされてしまいました。

Receive-Jobコマンドレットの変更点

前々回に、Invoke-Command -asJobで複数リモートコンピュータに対してジョブを走らせた場合、そのジョブに対して$job|Receive-Jobがなぜか機能しない、と書きましたがこの問題が解決されています。そもそもなんでこの問題が発生していたのか、面白いのでちょっと解説します。

実はReceive-Jobコマンドレットの-locationパラメータに「パイプライン入力を許可する   true (ByPropertyName)」フラグがついていたのが原因でした。複数コンピュータに対して実行したジョブは子ジョブを複数持ちますが、親ジョブ自体は配列ではありません。そしてそのLocationプロパティには子ジョブが実行されているコンピュータ名が"remote01,remote02,remote03"のようなカンマ区切りの文字列として格納されています。よってこのジョブオブジェクトをパイプラインを通じてReceive-Jobコマンドレットに渡すと、ValueFromPipelineByPropertyName属性が付いている-locationパラメータにジョブオブジェクトのLocationプロパティの値が渡されますが、その値はカンマ区切りの文字列なので正しく解釈されず、結果として期待の動作をしなかったわけです。

v3ではReceive-Job -locationのValueFromPipelineByPropertyName属性が取り除かれ、問題なく動作するようになりました。

他の変更点としてはReceive-Jobにジョブが完了するまで待つための-waitパラメータが追加されました。が、$job|Wait-Job|Receive-Jobと違いが分からないかも…。

Get-Jobコマンドレットの変更点

Get-Jobに-filterパラメータが追加されました。連想配列でジョブにフィルタをかけられるものです。

Get-Job -filter @{State="Completed";Location="localhost"}

where-objectを使わずともフィルタできるので便利、かも。しかし個人的には-filterパラメータはいろんなコマンドレットで定義されているものの、使い方がそれぞれ異なるのがとてもとてもイヤです。まず覚えられないのでヘルプを引くところから始まっちゃいますので。パフォーマンスの関係上、Where-Objectを使うよりコマンドレット内部でフィルタしたほうが速くなるというのはわかるのですが、もう少しフィルタ方式に統一性を持たせられなかったんだろうかとか思いますね。

Get-Jobにはほかに-afterと-beforeというパラメータが追加されています。これは後述するPSScheduledJobの完了時刻をDateTimeで範囲指定し、フィルタするものです。

PowerShell Workflow

PowerShell3.0というかWMF3.0のおそらく目玉機能の一つがPowerShell Workflowです。文字通り、PowerShellでワークフローが記述できるようになります。

Workflowは関数の一種なのですが、長時間を要するタスクやリモート実行や並列実行などで使うことを主目的としているようです。functionキーワードの代わりにworkflowキーワードでワークフローを定義すると、自動的に実行対象コンピュータ名や資格情報といったパラメータが複数定義されるので、これらのパラメータを特に定義なしで利用することができます。またworkflow内ではparallelブロックを定義でき、その中に記述された各行は並列に実行されます。またfor/foreachステートメントで-parallelパラメータが利用可能になり、繰り返し処理やコレクションの列挙を並列して行うことができるようになります。

自動定義されるパラメータに-asJobがあり、これを利用するとworkflowをジョブとして実行できます。このジョブは通常のジョブとは違い、新たに追加されたSuspend-JobコマンドレットとResume-Jobを使うことによって、ジョブの一時中断と再開ができます。このジョブの中断と再開は、リモートコンピュータ上でワークフローを走らせてるときでも可能ですし、中断後リモートセッションが切断されたあとに再開することもできますし、リモートコンピュータがシャットダウンしても再起動後にジョブを再開することまでできてしまいます。これらはWMFにおけるリモート基盤を支えているWinRMの最新バージョン、WinRM3.0が実現している機能です。このようにセッションを再接続してもタスクを継続できるような接続をrobust(堅牢な), resilient(弾力性のある、障害から容易に回復する) connectionと称しているようです。

PowerShell WorkflowはWindows Workflow Foundation(WF)と密接な関係があり、WFのデザイナで作ったxamlをPS Workflowに変換したり(逆もできる?)、Invoke-Expressionでxamlを実行したりできるらしいです。WF側でもPowerShellの多くの機能がアクティビティとして使用できたりして、WFとPowerShellがWMFというシステム管理フレームワークの主要なパーツとして密に連携していくようです。このあたりの話はWFの専門家であるAhfさんがPSアドベントカレンダーの23日目にしてくださる予定なので、楽しみですね!

なおPS Workflowは従来のPSスクリプトとは異なった利用状況を想定しているため、あるいはWFの機能と合わせるため、PSスクリプトではできるのにPS Workflowではできないことがとてもたくさんあります。forの中でbreakやcontinueステートメントが使えないとかStart-Sleepは-Secondパラメータしか指定できない(ミリ秒単位でスリープかけられない)とか色々あります。そのうちPS WorkflowとPSスクリプトの違いというドキュメントが公開されるんじゃないかと思います。

ちなみにWinRM3.0のおかげでワークフローではない通常のリモートジョブでも、New-PSSessionで作成したセッションの中でジョブを実行した場合、そのジョブが動作しているコンピュータへのセッションを切断(Disconnect-PSSession)したあと、セッションに再接続(Connect-PSSessionやReceive-PSSession)すればジョブの結果を取得したりすることができます。またセッションを作製したインスタンス(powershell.exe)でそのセッションを切断すると、それ以降は別のインスタンスやコンピュータからそのセッションにConnect-PSSessionで接続することができます。

ScheduledTasksモジュール

PowerShell3.0が含まれる次期Windowsでは大量のモジュールが追加され、それらのモジュールに含まれるコマンドレットの総数はWindows 8でも2000を超える膨大な量になります。これはWindows 8やWindows Server 8では従来のコマンドプロンプトから実行するコンソールexeコマンドのほとんどすべてをPowerShellコマンドレットに置き換える措置のためです。もちろん従来のコマンドは互換性のために残されますが、netsh.exeなど一部のコマンドではPowerShellへの移行を促すメッセージが表示されたりするようになるようです。参考:Window 8の機能の概要 − @IT

ScheduledTasksモジュールというタスクスケジューラを扱うモジュールもWindows 8 / Windows Server 8に新しく追加されるモジュールの一つで、schtasks.exeを置き換えるものとなります。これまでPowerShellでタスクスケジューラを扱うにはschtasks.exeを使うか、WMIのWin32_ScheduledJobを使う必要があり面倒でしたが、このモジュールに含まれるコマンドレットを用いるとそれが容易に行えるようになります。たとえば「notepad.exeを毎日朝10:00に起動する。バッテリ駆動のときでも実行」というタスクを「test」という名前で登録するには、

$action = New-ScheduledTaskAction -Execute "notepad.exe"
$trigger = New-ScheduledTaskTrigger -At "10AM" -Daily
$setting = New-ScheduledTaskSettings -AllowStartIfOnBatteries 
New-ScheduledTask -action $action -trigger $trigger -setting $setting|Register-ScheduledTask -TaskName test

とすれば可能であるはずです。実はServer 8 Developer Preview版ではこのコードは機能しません。タスクのトリガを作成するNew-ScheduledTaskTriggerコマンドレットが正しいオブジェクトを作ってくれないのです。これは将来のバージョンできっと修正されるかと思います。ただトリガを定義する部分をはずせば(あんまり意味はないですが)このコードは動作するので、やり方はたぶんあってると思います。

Register-ScheduledTaskコマンドレットには-asJobパラメータがあり、タスクスケジューラへの登録をジョブとしてバックグラウンドで行うことができます。ScheduledTasksモジュールはWMIを利用してタスクスケジューラを操作するので、ほかのWMI関係のコマンドレットと同様ですね。

なおScheduledTasksモジュールはデフォルトでは読み込まれていないので、使用するには本来Import-Moduleコマンドレットを使用しなければならないところですが、PowerShell3.0のCmdlet Discoveryという機能によりImport-Moduleは実行しなくてもScheduledTasksモジュールに含まれるコマンドレットを利用することができます。Cmdlet Discoveryとは現在読み込まれていて実行可能なコマンドレットの中にない、未知のコマンドレットを実行しようとしたとき、Modulesフォルダに存在するモジュールから同名のコマンドレットが定義されているものを探し出し、発見できたらそのモジュールを読み込んだうえでコマンドレットを実行するという優れた機能です。初回だけモジュールの検索とロードの手順が実行されるので待たされますが、一度Cmdlet Discoveryによってモジュールがシェルに読み込まれればあとは快適にコマンドレットを実行できるようになります。

PSScheduledJobモジュール

ScheduledTasksモジュールは-asJobパラメータが定義されているくらいで実はそれほどPowerShellのジョブとは関係ないのですが、ScheduledTasksモジュールが内包しているPSScheduledJobモジュールはPowerShellのジョブ機能と大いに関係があります。

従来PowerShellスクリプトをタスクスケジューラに登録するにはコマンドラインに"powershell.exe"を、引数に"-file hoge.ps1"を指定して、みたいなまわりくどいことをする必要がありました。しかし新しく追加されるPSScheduledJobモジュールに含まれるコマンドレット群はこの問題を解消します。PowerShellスクリプト(.ps1)あるいはスクリプトブロックをPSScheduledJobとして直接タスクスケジューラに登録できるようになり、PowerShellとタスクスケジューラのシームレスな連携を実現します。こちらはWindows 8/Server 8に付属のモジュールではなく、PowerShell 3.0に付属のモジュールなので、Win7などでも使用可能になる予定です。

使用例を見ていきましょう。

$triggers = @()
$triggers += New-JobTrigger -at "2012/01/01 11:11:10" -Once
$triggers += New-JobTrigger -at "10:00" -Daily

$sb = {
    "This is Scheduled Job."
    Get-Date
}

Register-ScheduledJob -ScriptBlock $sb -Trigger $triggers -Name ScheduledJobTest1

まずNew-JobTriggerコマンドレットによってトリガー(具体的には実行時刻など)を定義します。ここでは決められた時刻に1回実行するものと、毎日同じ時刻に実行するものの2つを定義してみました。そしてこれらの時刻に実行したい内容をスクリプトブロックに記述し、これらをRegister-ScheduledJobコマンドレットで登録してやります。

するとこのスクリプトブロックはタスクスケジューラに登録され、指定時刻になると指定したスクリプトブロックの内容が実行されます。このタスクは「タスクスケジューラ― ライブラリ\Microsoft\Windows\PowerShell\ScheduledJobs」に登録されています。

このタスクのアクションは具体的には次のようになっています。

powershell.exe -NoLogo -NonInteractive -WindowStyle Hidden -Command "Import-Module PSScheduledJob; Start-Job -DefinitionName 'ScheduledJobTest2' -DefinitionPath 'C:\Users\Administrator\AppData\Local\WindowsPowerShell\ScheduledJobs' -WriteToStore | Wait-Job"

これによると、指定時刻に実際にタスクスケジューラによって実行されるのはpowershell.exeであり、Start-Jobコマンドレットを使って登録したスケジュールをPowerShellのジョブとして実行していることがわかります。Start-Jobコマンドレットの-DefinitionNameパラメータなどはPSScheduledJobのために追加されたもので、これによりRegister-ScheduledJobが出力したPSScheduledJob定義をファイルから読み込んでジョブとして実行できるようになっています。PSScheduledJob定義とジョブの出力は-DefinitionPathで指定されているフォルダの下にxmlファイルとして保存されているので興味がある方は覗いてみるといいかもしれません。

さて、スケジュールしたジョブの実行結果はどうやって受け取ればいいのでしょうか。実はこれはすごく簡単で、PSScheduledJob(ここではScheduledJobTest1という名前で定義しました)がタスクスケジューラによって一度以上実行された後は、

$job=Get-Job -name ScheduledJobTest1

とすることでJobオブジェクトとして取得することができるようになります。あとは通常のジョブと同じ取り扱いができるので、

$job|Receive-Job

などで実行結果を取得できます。

ちなみにPSScheduledJobはそれを定義したインスタンス以外でも参照することができます。具体的にはpowershell.exeでジョブをスケジューリングして終了→また別のpowershell.exeを立ち上げてimport-module PSScheduledJobしたあとGet-Job|Receive-JobしてPSScheduledJobの結果を参照、みたいなことができます。

ここで紹介した一連の操作ではスクリプトブロックをPSScheduledJobにしましたが、Register-ScheduledJobコマンドレットの-FilePathパラメータを用いれば.ps1ファイルをPSScheduledJobとして登録することも可能です。

現行バージョンのPowerShellはとにかく起動が遅いため、タスクスケジューラにスクリプトを登録しても実行が始まるまで何十秒も待たされるなどはざらでしたが、PSv3は起動がずいぶん速くなり、スペックや状況にもよるとは思いますがpowershell.exeの起動後ほんの数秒でスクリプトが走り始めます。この速度のおかげもあってPSScheduledJobはきっととても有効に機能するんじゃないかと思います。

おわりに

今回はPowerShell 3.0で増強されるバックグラウンドジョブ関係の機能をまとめてみました。これらの新機能のおかげで、時間のかかる処理や定期実行する処理を扱うのが飛躍的にやりやすくなりそうです。PowerShell 3.0で追加される機能は他にもたくさんあって、このブログでもいつか全部紹介したいと思ってるのですが、今回取り上げたジョブ関係はその中でもかなり重要な機能増加を多く含んでいると言えるでしょう。PowerShell 3.0やWindows 8/Server 8のリリースに備えてジョブ関係から予習しておくのは悪くないと思いますよ。

なんか25日のアドベントカレンダーのうち3回もバックグラウンドジョブネタをやって、PSアドベントカレンダーというより私だけ一人でPSジョブアドベントカレンダーをやってる感じでちょっと申し訳ないんですが、どうか許してください。そして前回は今回で終了するって言ってたんですが、実はまだジョブ関係の小ネタが残ってるので最終日25日にさせてください。では今日のところはこのへんで。明日はwaritohutsuさんの登場です。よろしくお願いします。

2010/01/10

こんなビルドイベントはいかがでしょうか?

copy "$(TargetPath)" C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSTweets\
start powershell -NoExit -command "Import-Module PSTweets"

PSプロバイダやコマンドレットが含まれるdll(PSモジュール)をモジュールフォルダにコピーし、モジュールを読み込んだ状態でPowerShellを起動してくれます。ただし、systemフォルダに書き込む手順があるのでVisual Studioは管理者権限で起動してください。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/01/10/184828.aspx

2009/06/30

Windows Server 2008 R2ではPowerShell v2が標準機能ですが、v2の目玉の一つとしてリモートでサーバーのPSを実行できる機能があります。

詳しくは、7/4の大阪のセッションでデモを交えてお話しできると思いますが、http://technet.microsoft.com/ja-jp/magazine/2008.08.windowspowershell.aspxを読んで予習しておくのもいいかと思います。

なお、この記事が書かれたときの最新バージョンのCTP2から変更点があり、コマンドレット名が若干変わっています。

New-RunSpace→New-PSSession

push-runspace→Enter-PSSession

pop-runspace→Exit-PSSession

さて、PSRemotingはWinRM2.0を用いたリモート通信なのですが、WinRM2.0は認証にKerberos認証を用います。Active Directoryにログオンしたクライアントから

New-PSSession ServerName

のようにすると、ログオン時の認証情報が用いられ、認証され、通信が始まります。しかし、ログオン時とは異なる認証情報を送る必要がある場合もあるので、そのときは-credentialパラメータを使います。

New-PSSession ServerName -credential (Get-Credential)

のようにすると、認証ダイアログが表示されるので、ユーザー名とパスワードを入れて認証情報を送りログオンすることができます。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/06/30/176828.aspx

2006/11/15

WMIオブジェクトの正体、System.Management.ManagementObjectクラスにはtypes.ps1xmlファイルにスクリプトメソッドConvertToDateTimeが定義してあり、これを呼び出すことでWMIのCIMDateTime型を.NETのDateTime型に変換することができます。

$psexe = gwmi -class CIM_DATAFILE -filter "Name='c:\\windows\\system32\\windowspowershell\\v1.0\\powershell.exe' "
$psexe.ConvertToDateTime($psexe.LastModified)

このスクリプトメソッドの実体は、[System.Management.ManagementDateTimeConverter]::ToDateTime($args[0])なのですが、長いクラス名を省略できるのでよいですね。

ConvertFromDateTimeスクリプトメソッドというのもあります。何をするためのものかはあなたの想像通り。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/11/15/45497.aspx

2006/11/07

cmd.exeでdir /bとすると、カレントにあるファイル名・ディレクトリ名だけを出力します。以下、例。

C:\WINDOWS\system32\windowspowershell\v1.0>dir /b
certificate.format.ps1xml
dotnettypes.format.ps1xml
examples
filesystem.format.ps1xml
help.format.ps1xml
ja
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

同じことをPowerShellにやらせるにはどうすればいいか、考えてみました。

【案1】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|format-wide -c 1

    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS\system32\Win
    dowsPowerShell\v1.0

[examples]
[ja]
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Format-Wideコマンドレットを使った例。うーんちょっと違う。でもディレクトリに[]が付くのでわかりやすいかも。これはこれで。

【案2】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|select name
Name
----
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Select-Objectコマンドレットを使ってNameプロパティだけをもったPSCustomObjectのArrayを作っているのでこんな感じに出力されます。
Name
----
が邪魔ですね。

【案3】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|%{$_.name}
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

これでdir /bと同じ結果になりました。Foreach-ObjectでNameプロパティだけを取り出して出力しているわけです。

【案4】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|split-path -leaf
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Split-Pathコマンドレットを-leafオプション付きで使っても同様の出力が得られました。これ実は何故こうなるのかよくわからないんですけど、たぶんパイプを渡るときに、System.IO.DirectoryInfoオブジェクトやSystem.IO.FileInfoオブジェクトのNameプロパティが渡されてるか、ToString()メソッドが実行されているのでしょうね。ps1xmlファイルの何らかの記述でこうなっているのかもしれません。でもまあ仕組みが分からなくてもこの動作は非常に合理的であります。

【案5】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls -name
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

ていうかこんなことをしなくても、Get-ChildItemコマンドレットには-nameオプションがあり、dir /bと同じ効果が得られるのでした。ヘルプの見落としでこんな回りくどいことを考えていたのです。すみません、こんなオチで。でもまあ同じことをやるのに色んな手段があるということが分かったので良しとしましょう。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/11/07/43902.aspx


Copyright © 2005-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー

Twitter

Books