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

    }
}

WinRMでリストの端末に対して実行していってます。
中身読めば解りますけど、使用率に応じて色を変えて表示してます。

表示結果はこんな感じ。

(IPの部分はちょっと隠してますゴメンナサイ)



手動でさっと確認したいときには良い感じかも。

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

備忘録も込めたPowerShellネタです。

PowerShellでリモートPCを操作する時の方法は以下の3つみたい。

  1. <コマンドレット> -ComputerName
    • コマンドを指定のリモートPCに対して実行。
    • 主体はコマンドを投げる側。
    • 当然だが、「-ComputerName」がそもそも存在しないコマンドも多数。
  2. Invoke-Command -ComputerName -scriptblock{Get-Service}
    • 「-scriptblock{}」をリモートPCで実行。
    • 主体はリモートPC側。
  3. Enter-PSSession -ComputerName
    • 対話モード(SSHのログインみたいな感じ)でリモートPCに接続してゴニョゴニョ。
    • 主体はリモートPC側。

1で全ての業務が片付けば問題無しだが、まぁそうは問屋がおろさない。
んで、2を使ってリモートPCの操作をスクリプト化するんだけど、スコープがリモートPC側にあるので投げる側の変数がそのまま使用できない。


リモート側に「C:\Windows」は存在するが、

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

変数として渡してしまうとエラーとなる。

$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
コマンドのローカル変数の値を指定します。コマンドの変数は、リモート コン
ピューターでのコマンドの実行前にこれらの値で置換されます。値をコンマ区切
り一覧で入力します。値は、一覧されている順に変数に関連付けられます。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】Invole-Command –FilePath パラメタの魔法 - フィールドSEあがりの安納です - Site Home - TechNet Blogs

FizzBuzz問題をやってみる

先生に「手始めにお約束のFizzBuzz問題を解いてみるのはどうか|д゚)チラッ」って言われたので、プログラムの定番問題と呼ばれるFizzBuzz問題をやってみる

とりあえずPowerShellでやってみるかーということで書いてみる

# 3で割り切れる場合は 「Fizz」、
# 5で割り切れる場合は 「Buzz」、
# 両者で割り切れる場合は 「Fizz Buzz」 を数の代わりに発言する。

function fizzbuzz ([int]$a){
    for ( $i = 1; $i -le $a; $i++){
        #3の余剰
        $s = $i % 3
        #5の余剰
        $p = $i % 5
        
        if ( $p -eq 0 -and $s -eq 0 ){
            write-host "FizzBuzz"
        } elseif( $s -eq 0 ) {
            write-host "Fizz"
        } elseif( $p -eq 0 ) {
            write-host "Buzz"
        } else {
            write-host $i
        }
        
    }
}

#実行
fizzbuzz 100

15分位?で書きました。

おー。できたできたー。って喜んでグーグル先生にお問い合わせしたら、世の中の達人プログラマーは60文字以内とかで解いててひっそりと枕を涙で濡らしました。。

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

前回の続き。

参照されて処理を実行するスクリプト、実行スクリプトに引数を渡すスクリプト、サーバの定義が書いてあるxmlに別けて実装。

#==================================================
# イベントログ取得スクリプト
# 3日前までのイベントログを取得する
#==================================================

#--------------------------------------------------
#GetEventlog.ps1
#         第1引数:対象端末名
#         第2引数:イベントログ名(Application,System)
#--------------------------------------------------


function GetEventlog ([string]$_servername, [string]$_eventlogname)
{

    #変数定義---------------------
    $scriptName = "イベントログ取得スクリプト"
    #XMLファイル
    $XML = "C:\hogehoge\config\serverlist.xml"
    #引数
    $servername = $_servername
    $eventlogname = $_eventlogname
    #期間の設定
    $startdate = get-date
    $enddate = $startdate - (new-timespan -day 3)
    #ログファイルの出力先
    $filetime = get-date -format yyyyMMddhhmmss
    $file = "C:\temp\" + $servername + "_" + $eventlogname + "_" + $filetime + ".txt"

    
    try
    {
        #イベントログに開始ログを出力---------------------
        Write-EventLog "Application" "PowerShell Script" 0 -Message ($scriptName + "開始" + "`n" + "対象端末名は " + $servername + " です。" + "`n" + "対象イベントログは " + $eventlogname + " です。") -EntryType "Information"
        
        #XMLファイル読み込み---------------------
        $xmlDoc = [XML](Get-Content $XML)
        #XMLファイルの子要素を取得
        $config = $xmlDoc.get_DocumentElement()
        $childs = $config.get_ChildNodes()
        #xmlから値の取り出し
        #IPアドレス
        $ipaddress = $config.SelectSingleNode("server[@name='$servername']").ipaddress
        
        
        #イベントログ取得---------------------
        $logArray = get-eventlog -computername $ipaddress -logname $eventlogname -after $enddate -before $startdate | Select-Object EntryType,EventID,Source,TimeGenerated,Message
        
        #取得したイベントログをファイルに書き込む---------------------
        foreach ($row in $logArray){
            #Messageに入っている改行コードを変換
            $workMessage = [string]$row.Message.Replace("`n"," ")
            #日付の書式を整形
            $workTimeGenerated = [string]$row.TimeGenerated.ToString("yyyy/MM/dd HH:ss:mm")

            #タブ区切りで一行としてまとめる
            $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-EventLog "Application" "PowerShell Script" 0 -Message ($scriptName + "`n" + $file + "`n" + "を作成しました。") -EntryType "Information"
        } else {
        	#イベントログにファイルが無いことを書き込む
        	Write-EventLog "Application" "PowerShell Script" 0 -Message ($scriptName + "`n" + $file + "`n" + "が作成されていません。") -EntryType "Warning"
        }
    }
    catch [exception]
    {
        #イベントログに書き込んで終了
        Write-EventLog "Application" "PowerShell Script" 1 -Message ($scriptName + "`n" + $error[0]) -EntryType "Error"
    }
    finally
    {
        #イベントログに終了ログを書き込む
        Write-EventLog "Application" "PowerShell Script" 0 -Message ($scriptName + "終了" + "`n" + "対象端末名は " + $servername + " です。" + "`n" + "対象イベントログは " + $eventlogname + " です。") -EntryType "Information"
    }
    
}

上記「GetEventlog.ps1」は、引数に「端末名」と「イベントログ名」(Application,Systemなど)をもらい実行されるスクリプト
このファイルを「C:\hogehoge\script」に配置しておく。

途中で端末名とIPを紐付けるserverlist.xmlを読み込んでいるが、xmlの中身はこんな↓感じ。

  • サーバの定義が書いてあるxml
<?xml version="1.0" encoding="utf-16"?>
<config>
    <server name="hogehoge1">
        <ipaddress>192.168.1.1</ipaddress>
    </server>
    <server name="hogehoge2">
        <ipaddress>192.168.1.2</ipaddress>
    </server>
</config>

んで、GetEventlog.ps1を実行するスクリプト

#==================================================
# イベントログ取得スクリプト
#==================================================

#--------------------------------------------------
#GetEventlog
#         第1引数:対象端末名
#         第2引数:イベントログ名(Application,System)
#--------------------------------------------------

#インクルード
. C:\hogehoge\script\GetEventlog.ps1

#引数
$_servername = "hogehoge1"
$_eventlogname = "Application"

#実行
GetEventlog $_servername $_eventlogname

出力される実行結果テキストは以下な感じ。

2012/06/18 12:00:01	Information	6013	EventLog	システムの稼働時間は 98037 秒です。
2012/06/18 08:46:35	Warning	36	W32Time	ソース 'W32Time' のイベント ID '-2108030940' の説明が見つかりません。必要なレジストリ情報またはメッセージを表示するメッセージ DLL ファイルがローカル コンピュータに存在しない可能性があります。または、これらのデータへのアクセス許可がユーザーに与えられていない可能性があります。次の情報はイベントの一部です:'86400'
2012/06/18 00:31:35	Error	48	W32Time	ソース 'W32Time' のイベント ID '-2108030928' の説明が見つかりません。必要なレジストリ情報またはメッセージを表示するメッセージ DLL ファイルがローカル コンピュータに存在しない可能性があります。または、これらのデータへのアクセス許可がユーザーに与えられていない可能性があります。次の情報はイベントの一部です:'time.windows.com,0x1', '到達できないホストに対してソケット操作を実行しようとしました。 (0x80072751)', '960'

後は、引数を渡すスクリプトを 端末数 × イベントログの数 だけ用意すれば良い感じにしてみた。

と、windowsのメモ帳で開くと良い感じなんだけどsakuraエディタみたいなので開くと改行コードが色々で、変な所で改行されてたりする。。。

ココらへんの改修はまた今度(´・ω・`)

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

「イベントログを取得するバッチ作って」って言われました。

イベントログ開いて右クリック→別名で保存でいいんじゃまいかとも思いましたが、まぁご要望らしいので。


イベントログを取得するコマンドは以下。

名前
    Get-EventLog

概要
    ローカル コンピューターまたはリモート コンピューター上のイベント ログまたはイベント ログの一覧のイベントを取得しま
    す。


構文
    Get-EventLog [-AsString] [-ComputerName <string[]>] [-List] [<CommonParameters>]

    Get-EventLog [-LogName] <string> [[-InstanceId] <Int64[]>] [-After <DateTime>] [-AsBaseObject] [-Before <DateTime>]
     [-ComputerName <string[]>] [-EntryType <string[]>] [-Index <Int32[]>] [-Message <string>] [-Newest <int>] [-Source
     <string[]>] [-UserName <string[]>] [<CommonParameters>]


説明
    Get-EventLog コマンドレットは、ローカル コンピューターおよびリモート コンピューター上のイベントおよびイベント ログ
    を取得します。

    プロパティ値を使用してイベントを検索するには、Get-EventLog のパラメーターを使用します。Get-EventLog は、指定された
    プロパティ値のすべてと一致するイベントのみを取得します。

    EventLog という名詞を含むコマンドレット (EventLog コマンドレット) は、従来のイベント ログでのみ有効です。Windows Vi
    sta 以降のバージョンで Windows イベント ログ技術を使用しているログからイベントを取得するには、Get-WinEvent を使用し
    ます。

簡単にイベントログを引っ張ってこれるんだけど、結構要らない情報もついてくる。
一旦、CSVXMLにexportしてから任意の列を取り出そうなんてコソコソ考えてたんだけど、同僚がもうちょっとシンプルに欲しい情報だけ引っ張ってこれるよーと教えてくれたのが以下。

#アプリケーションイベントログを取得
$logArray = Get-EventLog -ComputerName 192.168.0.1 -LogName "Application"  | Select-Object EntryType,EventID,Source,TimeGenerated,Message 

こんな感じに取得できる。

EntryType     : Information
EventID       : 9003
Source        : Desktop Window Manager
TimeGenerated : 2012/06/14 13:26:33
Message       : 合成テーマが使用されていないので、デスクトップ ウィンドウ マネージャを開始できませんでした

EntryType     : Information
EventID       : 9003
Source        : Desktop Window Manager
TimeGenerated : 2012/06/14 13:26:33
Message       : 合成テーマが使用されていないので、デスクトップ ウィンドウ マネージャを開始できませんでした
  • EntryType Error,Warning で、エラー警告のみとか、
  • newest 5 で、最新の5件とか、
  • After,-Before で日付を絞ったりできるのでお好みで。


続いてファイル出力。

#1件ずつ取り出して、ファイルに1行づつ書き出す
foreach ($row in $logArray)
{
    $line = [string]$row.EntryType + "," + [string]$row.EventID + "," + [string]$row.Source + "," + [string]$row.TimeGenerated + "," + [string]$row.Message
    #ファイル書込み
    $line | Out-File -filepath C:\tmp\eventLog.txt  -Append
}

C:\tmp\eventLog.txt の中身はこんな感じ。

Information,9003,Desktop Window Manager,06/14/2012 15:10:10,合成テーマが使用されていないので、デスクトップ ウィンドウ マネージャを開始できませんでした
Information,9003,Desktop Window Manager,06/14/2012 15:10:10,合成テーマが使用されていないので、デスクトップ ウィンドウ マネージャを開始できませんでした

select-objectで欲しい情報を選択して、foreachで回しながらそれぞれのオブジェクトをカンマ区切りで一行にしてtxtに追記していく。
あとはこのtxtをExcelにでも取り込んでいただければ調査しやすいと思う。

と、なかなか良い感じなのだが、Message(本文)の中に改行コードが入っているとそのままファイルに出力されてしまい、ちょっと残念な感じになってしまう。

改行コードを何とかしたいと思ったところで時間切れとなったのでまた明日格闘してみる。



それにしても、はてなってpowershellのハイライト表示とかできないのかなぁ?