2013年4月10日水曜日

Invoke-CommandでリモートPCに引数を渡す

PowershellでリモートPCを操作する方法は以下の3つらしい。


  1. <コマンドレット> -ComputerName <リモートPC>
  2. Invoke-Command -ComputerName <リモートPC> -scriptblock{Get-Service}
  3. Enter-PSSession -ComputerName <リモートPC>


1は、コマンドレットに「-ComputerName」があればそのまま使用できるタイプ。
PowerShellでイベントログを取得する」で使用した「Get-Eventlog」とかとか。
当然だが、全てのコマンドレットにあるわけではない。スコープはサーバ側。

2は、「-scriptblock{}」をリモートPCで実行できるコマンドレット。
スコープはクライアント側。

3は、対話モードでリモートPCに接続するコマンドレット。
リモートPCにログインするような形で接続してゴニョゴニョできる。当然スコープはクライアント側。


で、やっぱり1だけではリモートPCの管理を全てスクリプト化することは難しいので、2を使用することになるんだけど、ここで問題になったのがよくあるスコープの話。

例を書くと、

$hostname = "192.168.1.2"
Invoke-Command -Computername $hostname -Scriptblock{Test-Path C:\Windows}
True

リモートPC側に「C:\Windows」ってフォルダはあるんだけど

$hostname = "192.168.1.2"
$a = "C:\Windows"
Invoke-Command -Computername $hostname -Scriptblock{Test-Path $a}
引数が null であるため、パラメーター 'Path' にバインドできません。
    + CategoryInfo          : InvalidData: (:) [Test-Path]、ParameterBindingVal 
   idationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M 
   icrosoft.PowerShell.Commands.TestPathCommand
    + PSComputerName        : 192.168.1.2

引数として書いてしまうとそんな変数なんも値ないよと。。

ということで、Invoke-Commandのヘルプとにらめっこ。

すること数分。
あやしいのを発見|д゚)チラッ



 -ArgumentList <Object[]>
        コマンドのローカル変数の値を指定します。コマンドの変数は、リモート コン
        ピューターでのコマンドの実行前にこれらの値で置換されます。値をコンマ区切
        り一覧で入力します。値は、一覧されている順に変数に関連付けられます。Argu
        mentList のエイリアスは "Args" です。
     
        ArgumentList の値は、"1024" などの実際の値にすることも、"$max" などのロ
        ーカル変数の参照にすることもできます。
     
        コマンドでローカル変数を使用するには、次のコマンド形式を使用します。
        {param($<名前 1>[, $<名前 2>]...)<ローカル変数を持つコマンド>} -Argument
        List <値 | $ローカル変数>
     
        "param" キーワードには、コマンドで使用するローカル変数を列挙します。Argu
        mentList パラメーターには、変数を列挙した順にその値を指定します。


さっそく実行。配列で渡るということなので

$hostname = "192.168.1.2"
$a = "C:\Windows"
$b = "C:\Program Files"
Invoke-Command -Computername $hostname -Scriptblock{Test-Path $args[0];Test-Path $args[1]} -ArgumentList $a,$b

True
True


とりあえず上記を使用すれば目的は達成できそうです。


PowerShellでイベントログを取得する

「なにを今更。Get-Eventlog使うだけじゃん!」
なんてツッコミが飛んできそうですけど。。

いや、確かに使いますよ。げっといべんとろぐ。

とりあえず、こんな感じで欲しい情報は取得できます。

Get-EventLog -ComputerName 192.168.0.1 -LogName "Application"  | Select-Object EntryType,EventID,Source,TimeGenerated,Message


そして今回のお客様のご要望は以下。
  • 何日前までとか指定したい
  • さっと見れるようにテキストに結果吐いて

ということで、こんな感じで実装しました。
$IpAddress = "192.168.0.1"
$EventLogName = "Application"
$StartDate = Get-Date
$EndDate = $StartDate - (New-TimeSpan -day 6)
$file = "C:\" + $EventLogName + ".txt"

#イベントログ取得
function getEventLog ($IpAddress,$EventLogName,$StartDate,$EndDate,$file)
{
    #イベントログ取得
    $logArray = Get-EventLog -Computername $IpAddress -logname $EventLogName -after $EndDate -before $StartDate | Select-Object EntryType,EventID,Source,TimeGenerated,Message
    #期間内のイベントログが無い場合は、ファイルにログ無しと書き込む
    if ($null -eq $logArray)
    {
        $message = "イベントログがありません。 期間:" + $startdate + " ~ " + $enddate
        $message | Out-File -Filepath $file
    }
    else
    {
        #取得したイベントログをファイルに書き込む
        foreach ($row in $logArray)
        {
            #Messageに入っている改行コードを変換
            $workMessage = [string]$row.Message.Replace("`n"," ")
            #日付の書式を整形
            $workTimeGenerated = [string]$row.TimeGenerated.ToString("yyyy/MM/dd HH:mm:ss")
            
            #一行としてまとめる
            $line = $workTimeGenerated + "`t" + [string]$row.EntryType + "`t" + [string]$row.EventID + "`t" + [string]$row.Source + "`t" + $workMessage
            
            #ファイルに書き込む
            $line | Out-File -Filepath $file  -Append
        }
    }
    #ファイルの存在チェックをして終了
    if (Test-Path $file)
    {
        #ログを出力
        Write-Host $file "を作成しました。"
    }
}

#実行
getEventLog $IpAddress $EventLogName $StartDate $EndDate $file


ガッと取ってきて一行づつ取り出して、日付を整形して、本文の改行コードを変換してテキストに追記していってます。

2013年4月9日火曜日

Write-Host で表示されるメッセージに色をつける


なんとなく検索していたら面白いもの見つけたのでメモメモ。

Write-Host コマンドレットの使用

Write-Hostで表示されるメッセージに色つけれるみたい!

Write-Host "コメント" -foregroundcolor red -backgroundcolor yellow 

ということで、各サーバのCドライブの利用状況を色別けして表示させてみるスクリプトとか作ってみた。


function MyFunction
{

    $serverlist = @("192.168.1.1"; "192.168.1.2"; "192.168.1.3")

    #サーバリストの分だけループ
    foreach ($ipaddress in $serverlist)
    {
        Write-Host "`n"
        Write-Host "★☆★-----------------【" $ipaddress "】-----------------★☆★" -foregroundcolor red -backgroundcolor yellow 

        #実行
        #Cドライブの使用状況を表示する
        CheckDiskSize

        Read-Host "実行結果を確認して、エンターを押してください!"
    }
}

function CheckDiskSize
{
    Write-Host "■□■ Cドライブの使用状況を表示します。 ■□■"
    #Session作成
    $Session = New-PSSession -ComputerName $ipaddress
    
    #コマンド実行
    Invoke-Command -Session $Session -Scriptblock{
        $drive = $args[0]
        #ドライブの容量チエック
        $used = (Get-PSDrive $drive).Used / 1GB
        $free = (Get-PSDrive $drive).Free / 1GB
        #使用率(四捨五入)
        $rate = [math]::Truncate((($used / ($free + $used)) * 100)+.5)

        #メッセージ作成
        $Message = $scriptName + "`n" + "`n" + 
            $drive + "ドライブの使用量:" + ([math]::Truncate($used)+.5) + "GB" + "`n" + 
            $drive + "ドライブの空き容量:" + ([math]::Truncate($free)+.5) + "GB" + "`n" + 
            $drive + "ドライブの使用率:" + $rate + "%"

        #使用率が70%以上で80%未満だったら黄色で表示
        if ($rate -gt "70" -and $rate -le "80")
        {
            Write-Host $Message -foregroundcolor Yellow
        }
        #使用率が80%以上だったら赤字で表示
        elseif ($rate -gt "80")
        {
            Write-Host $Message -foregroundcolor Red
        }
        #それ以外はセーフ
        else
        {
            Write-Host $Message -foregroundcolor Green
        }
    } -ArgumentList "C"
    
    #Session削除
    Remove-PSSession $Session

    }
}

#実行
MyFunction

WinRMで$serverlistの端末に対してドライブの容量チェックを実行していきます。
使用率に応じてコンソールに表示される色を変えています。



手動でサッと確認したいときに便利かと。

BloggerでPowerShellコードをシンタックスハイライトする

とりあえずGoogle先生にお尋ねしてたどり着いた先が↓のサイト様でした。

How to Add Syntax Highlighter(v3) to Blogger Blogs

上記サイトの「Syntax Highlighter Scripts Generator」で「PowerShell」にチェックを入れてJSを生成して頂き、Bloggerのテンプレートの</head>の上に書き込むと。
(まぁ使い方に書いてありますね…)

で、実際にPSのコードを記載する際には、


<pre class='brush:powershell;'>
 $OutputParh = "C:\hogehoge"
 New-Item $OutputParh -ItemType Directory
</pre>

みたいに<pre>タグで囲って、classに「brush:powershell」と書いてあげると、

 $OutputParh = "C:\hogehoge"
 New-Item $OutputParh -ItemType Directory



ふぅ。めでたしめでたし。

引越しました。

仕事の関係上、powershellとかC#ネタの投稿が多いと思うのですが、 はてなはpsのシンタックスハイライトに対応していないのでこちらにお引越しします。 

向こうで書いたネタも修正してこちらでアップし直します。