2011年9月29日木曜日

◆Write-Verboseで詳細情報を書き込む

Verboseパラメータは詳細情報を表示する共通パラメータとして、どのコマンドレットでも使用できる。(実際に有効なメッセージが表示されるかどうかはコマンドレットの実装次第だと思うが)

この機能を自作のコマンドレットや関数などに実装するためのコマンドレットが「Write-Verbose」(だと思う)

このコマンドレットでメッセージを書きこんでおくことにより、Verboseパラメータでの詳細情報取得が可能になる。

目的が合っているかどうかはさておき、使い方は以下のような感じ。

001
002
003
004
005
006
007
008
009

function directory($path,[switch]$verbose)
{
 
dir $path | tee -variable files
  Write-Verbose ($files | group{$_.extension} | Out-String ) -Verbose:$verbose
}
"◆◆◆normal---"
directory $pshome 
"◆◆◆verbose--"
directory $pshome -verbose

image

ちなみに、SwitchパラメータをTrue、Flseで指定するには、
「パラメータ名:$true」の様にコロンに続けて指定する。

2011年9月27日火曜日

◆Powershellで「.NET Framework 4.0」を使う

PowerShell: ◆Join-Pathの引数を配列対応にするで、ふと本家.NETの方はどうなのだろうとちょっと調べたら、Ver4.0から配列のPathを繋ぐことが出来るらしい。

2011-09-27 18h16_38

ただし、Powershellでは.NETの2.0が使われているようなのでそのままではこのオーバーロードを使うことは出来ない。

4.0を使うにはっと調べたら、単純にApp.Configを書けば良いらしい。
こんな感じのようだ。

<?xml version="1.0"?>  
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0.30319"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>


こいつを、




PS>notepad (Join-Path $pshome powershell.exe.config)


で、書きこんでやればOK


2011-09-27 18h26_03


まぁ、そこまでしてポータブルじゃないコードを書く必要もないと思うのでCombine自体は使わないと思うが、「.NET 4.0」を使う方法もあるということは覚えておいても良いのかもしれない。

2011年9月26日月曜日

◆Join-Pathの引数を配列対応にする

ファイル処理を行なっているとJoin-Pathをネストして使いたい時が結構ある。Pathを3つとか4つとか指定していっぺんにJoinしてくれると良いのだが、どうもそのような機能は無さそう。

ということで簡単な関数を作ってみた。

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019

function Join-MPath
{
 
if(!$args.Count){return
}
 
$rtnPath = $args[0]
  foreach($p in $args[1..$args.Count]
)
  {
   
$rtnPath = Join-Path $rtnPath $p
  }
 
$rtnPath
}

"---"
Join-MPath 
"---"
Join-MPath "c:" 
"---"
Join-MPath "c:" "windows" 
"---"
Join-MPath "c:" "\windows\" "\system32"

2011-09-26 10h22_57

関数自体は何の変哲もないが、個人的には結構便利。


牟田口さんからコメントを頂きました。

いつも有益な情報をありがとうございます。
パイプで繋ぐ方法、良いですね。Powershellっぽくて。

使わせてきただきます。(__)

2011年9月20日火曜日

◆パラメータをフォワードする

Forwarding Parameters - Power Tips - Powershell.com Powershell Scripts, Tips and ResourcesにいかにもTipsというのがあったので紹介。

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018

function dir-name($path, $include,$erroraction) {
Get-ChildItem -Path $path -Include $include -ErrorAction $erroraction
}

function dir-name2($path, $include,$erroraction) {
Get-ChildItem @PSBoundParameters
}

function dir-name3($path, $include,$erroraction) {
Get-ChildItem @PSBoundParameters -Exclude "d*"
}

"dir-name"
dir-name ".\*" "*.accdb" 
"continue"
"dir-name2"

dir-name2 ".\*" "*.accdb" 
"continue"
"dir-name3"

dir-name3 ".\*" "*.accdb" "continue"

image

関数の引数で受け取ったパラメータを内部で実行するコマンドレットのパラメータにそのまま渡す例である。

dir-name関数はごく普通に指定した場合。

dir-name2では分配演算子を使って$PSBoundParameters自動変数をコマンドレットに引き渡している。
$PSBoundParameters自動変数は関数に渡された引数が連想配列で保持されている。

パラメーターを追加して実行する場合はdir-name3の様にすれば良い。

2011年9月13日火曜日

◆ソートのカスタマイズ

ActiveDirectoryからユーザー情報を取ってきて表示する際に独自の並び順で並べたいことがある。

例えば、役職などでは基本的に偉い順で並べたいのだが、単純に日本語で登録している場合は意図したとおりには並ばない。

幸い、Sortコマンドレットはソートプロパティに集計プロパティが指定できるのでそれを使うとこんな感じでカスタマイズが可能だ。

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016

$dmAUsers = Get-QADGroupMember -Identity "Domain users" -Service "A.hoge.co.jp"
$dmBUsers = Get-QADGroupMember -Identity "Domain users" -Service "B.hoge.co.jp"

$dmAUsers + $dmBUsers   |
 
 
sort domain,department,
     @{expression=
{
     
switch ($_.
title){
       
"本部長"{1;break
}
       
"部長"{2;break
}
       
"副部長"{3;break
}
       
"主任"{4;break
}
       
"係員"{5;break
}
       
default{6;break
}
      }
    }} 
|
    select domain,department,title,name,email,samaccountname
<!--EndFragment

他ドメインの情報も必要だったのでPowerQuestの拡張コマンドを使ってユーザーを取得している。

ソートコマンドレットで並べたい順番に数字に置き換えてあげれば独自の並び順を実現できる。

◆ActiveDirectory、無効のアカウントをフィルターする

Search-ADAccountコマンドレットのAccountDisabledパラメータを使えば無効でないアカウントを取得することは可能だが、既に取得済みのユーザーに対してフィルターしたいこともある。

有効、無効を判定する属性はuserAccountControl属性として以下のように保持しているようだ。
image

最下位2ビット目がそれっぽいのでフィルターすると以下のようになる。

001
002
003

Get-ADGroupMember "Domain Users"| 
Get-ADUser -prop name,userAccountControl | 
?{!(($_.userAccountControl -band 0x00002) -eq 0x00002)}

ちなみに、PowerShell: ◆ActiveDirectoryグループごとのメンバー一覧を表示する2で使ったPowerQuestの拡張コマンドを使うと以下のように簡単にフィルターできる。

001
Get-QADGroupMember "Domain Users"| ?{!$_.AccountIsDisabled}

ただし、拡張コマンドは全般的に機能が豊富な分若干重い感じがするのでケースバイケースで使い分けが必要かもしれない。

--

あれ、単純にEnabledプロパティを使えばよいだけ?(汗)

2011年9月9日金曜日

◆PowershellでSharePointサイトのクロール処理を実行する

SharePointのプロキシー設定がメタメタで、色々と調査するのに何度も手動クロールする必要があったため、Powershellスクリプトを作ってみた。
特に、IEのプロキシー設定を変える必要があり、SharePointの管理サイトにつながらない環境でテストするには必須になる。

001
002
003
004
005
006
007
008
009
010
011
012

$sApp = Get-SPEnterpriseSearchServiceApplication "Search Service Application"
$cSource = Get-SPEnterpriseSearchCrawlContentSource
 `
 
-Identity "ローカルの SharePoint サイト" -SearchApplication 
$sApp
$cSource
.StartFullCrawl()
cls
Write-Host
 "■クロールスタート"
Do
{
 
Start-Sleep 3
  Write-Host "*" -NoNewline
}
until($cSource.CrawlStatus -eq "Idle")
$cSource | fl *
<!--E

まずは1行目でサーチサービスアプリケーションのインスタンスを取得。
そのインスタンスを使って2行目でコンテンツソースを取得。
あとは取得したコンテンツソースのStartFullCrawlメソッドを呼んでやるだけ。
終了イベントはなさげだったのでループして監視。
結果はコンテンツソースオブジェクトから取得できる。

この例では一つのソースだけを使っているが、複数のソースを纏めてクロールするには、2行目でIdentityパラメータを省略するとソース全部が返ってくるので、それをループしてやれば良さそうだ。

◆FileのTemporary属性をクリアする

エクスプローラーでファイルを検索していてなぜかヒットしないファイルが有ることに気がついた。

例えば、以下のような状態で検索ボックスに「Audi.jpg」と入れてもヒットしないのだ。
image

結果、
image

最初はWindowsの検索機能の不具合か、検索設定が何かしらあるのかと色々と調べたが、どうもファイル自体に問題があるような感じ。

プロパティを見ると、セキュリティブロックがかかっていた。
image

なんだ、これかと思ったのだが、こいつを解除しても状況は変わらず・・・。

しかし、ブロックされていたということはインターネットからダウンロードしたりメール添付されて着たファイルと思われる。
やはり何か特別な状態に成っている可能性大。
ファイル属性を他のファイルと比較してみると「Temporary」という属性が付けられている。
image

どうやらこいつが犯人っぽい。
このTemporary属性が付いているとファイルサーバーのレプリケートの対象などからも外れるらしい。
いったい誰がこんな属性を付けたのかと思えばなんとサードパーティではなくOUTLOOK君。
相変わらず「いけてない」

まぁ、文句を言っても仕方が無いのでこのTemporary属性を削除してみる。
Archive等の属性であればSet-ItemPropertyとかで設定できるっぽいのだが、
13.ファイルの属性を設定する < ファイル操作 Tips メニュー < PowerShell Tips < HIRO's.NET
Temporary属性はこいつでは設定できないようだ。
以下のようにフラグを直接操作してあげる必要がありそう。

001
002

$tFile = Get-Item w:\test\Audi.jpg 
$tFile.attributes = ($tFile.attributes -band 0xFEFF)

フラグの詳細は以下を参照。

ファイルを纏めて操作するときはこんな感じで。

001
002
003
004
005

Get-childitem w:\test -recurse | 
%{if (($_.attributes -band 0x100) -eq 0x100
) {
        
$_.attributes = ($_.attributes -band 0xFEFF)
      }
 }

2011年9月6日火曜日

◆C#からPowershellコマンドレットを使う

まずはPowershell用のdll(System.Management.Automation)を参照設定。
このdllは通常、
C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
にある。

あとはスクリプトを文字列で定義してRunspaceInvokeクラスのInvokeメソッドに渡してやるだけだ。
結果はCollection<PSObject>で返ってくるのでPSObjectを実際の型にキャストしてあげればOK。
目的にもよるのだろうが、単一値の取得などであればPowershellの方で文字列まで変換して返したほうが簡単な気がする。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
//            string script = @"Write-Output 'Start ----'
//                              ps | %{$_.name}";
            string script = @"ipmo ActiveDirectory
                              Get-ADUser test | %{$_.name}";

            RunspaceInvoke ivk = new RunspaceInvoke();
            Collection<PSObject> psList = ivk.Invoke(script);
            foreach (var pso in psList)
            {
                //string st = pso.BaseObject.ToString();
                string st = pso.ToString();

                Console.WriteLine("TestUserName:{0}",st );
            }

            Console.Write("Enter eny key");
            Console.ReadLine();
        }
    }
}

image

2011年9月1日木曜日

◆Hyper-Vの仮想マシンをバックアップする

仮想マシンをシャットダウンして、バックアップして、再び起動するというスクリプトを作ってみた。

標準ではPowershellからVMを操作するコマンドはなぜか用意されていない。
そこで、PowershellからVMを操作するにはWMIを使用するか、CodePlexで提供されているツールを使うか、システムセンターを使うかなのだそうだ。
ここらへんの話は以下の記事で詳しく解説してある。
【ハウツー】【管理効率化への挑戦】PowerShellで仮想環境も管理!! Hyper-V、SCとの連携 (1) Hyper-Vマネージャの問題点 | エンタープライズ | マイコミジャーナル

今回はバリバリ仮想マシンを操作するわけではないので、追加モジュールの必要ないWMIを使うことにした。

function main
{
    $ErrorActionPreference = "STOP"

    $VMName = "GSG-SVV2"
    $VMQuery = "SELECT * FROM Msvm_ComputerSystem WHERE ElementName='$($VMName)'"
    $VMObj = gwmi -Query $VMQuery -Namespace "root\virtualization" -Computername "."
   
    Write-Output "■仮想マシン($($VMName))バックアップ開始:$(Get-Date)▼▼▼▼▼▼▼▼"
    if($VMObj.EnabledState -eq 2)
    {
        shutdown-vm
        Start-Sleep 300
    }
    bkupcopy
    startup-vm
    Write-Output "■仮想マシン($($VMName))バックアップ終了:$(Get-Date)▼▼▼▼▼▼▼▼"
   
    trap{
      Send-MailMessage    -To "<vmuser@hoge.co.jp>"                     `
                    -From "VMServer <admin@hoge.co.jp>"            `
                    -Subject "VM Bkup Error"                     `
                    -SmtpServer "smtp.hoge.co.jp"                             `
                    -Body "仮想マシン($($VMName))バックアップエラー" `
                    -Encoding  ([System.Text.Encoding]::Default)
    }   
}

function shutdown-vm
{

    $STDObjQuery = "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$($VMObj.name)'"

    $STDObj = gwmi -Query $STDObjQuery -Namespace "root\virtualization" -Computername "."
    $Result = $STDObj.InitiateShutdown($true,"バックアップ")
}


function bkupcopy
{
    $gen = [Math]::Floor(((Get-Date).DayOfYear/7))%3 + 1
    $bkupPath = "E:\仮想マシンバックアップ\gen$gen"
    if(Test-Path $bkupPath){cmd /C "rmdir /S /Q $bkupPath"}
    copy (Join-Path "D:\VM" $VMName) $bkupPath -force -Recurse
}

function startup-vm
{
    $VMObj.RequestStateChange(2)
}

. main >> c:\log\仮想マシンバックアップ.log 2>&1

毎週末に実行し、3世代管理することとした。(年初からの日数で週数を求めて世代としている)

コピーする前に前回のコピーをクリアしているのだが、PowershellのRemove-Itemを使うとなぜか上手く行かなかった。
どうも、非同期に動作して次行のCopyとぶつかるような動作をしてしまうのだ。
それで止む無くDOSコマンドを使ってクリアしている。

仮想マシンのステータスはこんな感じらしい。

Enabled (Turns the VM on) : 2
Disabled (Turns the VM off) : 3
Reboot (A hard reset of the VM) : 10
Reset (For future use) : 11
Paused (Pauses the VM) : 32768
Suspended (Saves the state of the VM) : 32769

起動時はこのステータスを2にしてあげれば良いのだが、シャットダウンは3にすれば良いというものでもないらしい。
(シャットダウンオブジェクトを取得してそのメソッドを呼び出している)

また、シャットダウンは非同期に動くようなのでとりあえずSleepを噛ませている。

◆SharePoint、サーバーファームをバックアップする

ファームのバックアップは、Backup-SPFarmコマンドレットを使う。

Backup-SPFarm -BackupMethod full `
  -Directory \\gsg-sv\ファームバックアップ -BackupThread 3 -ea Stop

BackupMethodでフルバックアップか差分バックアップかを指定。
Directoryがバックアップ先の場所。
BackupThreadはGUIで行うときのデフォルトが3だったので同じ値を指定。

バックアップのログを吐きつつエラーの時は管理者にメールするなんてのが普通のパターンだろうか。

& {
    trap{
    Send-MailMessage    -To "<spuser@hoge.co.jp>"                     `
                    -From "SharePoint <spadmin@hoge.co.jp>"            `
                    -Subject "SharePoint Farm Bkup Error"                     `
                    -SmtpServer "smtp.hoge.co.jp"                             `
                    -Body "SharePointファームバックアップエラー($(hostname))" `
                    -Encoding  ([System.Text.Encoding]::Default)

    }
    Add-PSSnapin Microsoft.SharePoint.PowerShell -ea 0
    Write-Output "■ファームバックアップ開始:$(Get-Date)▼▼▼▼▼▼▼▼"
    Backup-SPFarm -BackupMethod full `
      -Directory \\gsg-sv\ファームバックアップ -BackupThread 3 -ea Stop
    Write-Output "■ファームバックアップ終了:$(Get-Date)▲▲▲▲▲▲▲▲"
} >> c:\log\ファームバックアップ.log 2>&1

◆リダイレクト

リダイレクトはDOSコマンドと同様にリダイレクト演算子が使える。

dir  >  a.txt

エラー出力をリダイレクトする時はこんな感じ。

dir  hoge  2>  a.txt

標準出力とエラー出力を纏めてリダイレクトも出来る。

dir  c:\,hoge  >  a.txt  2>&1

複数のコマンドレットの結果を纏めてリダイレクトしたい時にそれぞれのコマンドレットにリダイレクト指定をするのはちょっと面倒。
関数などにまとめれば良いのだろうが、簡便的には以下のようにスクリプトブロックにしてしまえば纏めてリダイレクトできそうだ。

& {
    dir c:\
    dir hoge
} > a.txt 2>&1