Operations Lab.

Posts Tagged ‘PowerShell 3.0

PowerShell スクリプトのコマンドヘルプの書き方(簡易版)

leave a comment »

PowerShell では Get-Help コマンドレットでコマンドのヘルプを出力できます。自作したスクリプト(モジュール化などはしていない、単純な ps1 スクリプトファイル)であっても、ヘルプ情報を埋め込むことができます。

書き方

ヘルプ トピックの作成方法は、about_Comment_Based_Help に記載されています。

スクリプトファイルの先頭(Requires の直後)に、ブロックコメントで必要な情報を記載します。(ブロックコメントではなく 1 行ずつコメント化しても認識しますが、複数行に渡ることが多いため通常はブロックコメントを使用します。)

記述方法のサンプルは以下の通りです。

<#
    .SYNOPSIS
        Windows Server 2016 RTM の NanoServerImageGenerator を修正し、インターフェース名が ASCII 以外の場合にもアドレスを設定できるようにします。
    
    .DESCRIPTION
        Windows Server 2016 RTM の NanoServerImageGenerator は、インターフェース名が ASCII 文字列以外で表現されることを想定していません。そのため、デフォルトインターフェース名が ASCII 外の環境、例えば "イーサネット" となっている日本語環境では、IP アドレスを正しく設定できません。

        このスクリプトは、NanoServerImageGenerator モジュールを修正し、インターフェース名が ASCII 以外の環境においても、New-NanoServerImage で指定した IP アドレスが正しく設定されるよう、モジュールを修正します。
    
    .PARAMETER Path
        NanoServerImageGenerator モジュールのフォルダーを指定します。指定したフォルダー内のモジュールファイルが直接修正されます。
    
    .INPUTS
        なし。パイプラインからの入力には対応していません。
    
    .OUTPUTS
        System.Int32
        正常終了した場合は 0 を、それ以外の場合は 1 を返します。
    
    .EXAMPLE
        .\Modify-NanoServerImageGenerator.ps1 -Path C:\Temp\NanoServer\NanoServerImageGenerator
        C:\Temp にインストールメディアの NanoServer フォルダーをコピーした場合、NanoServer フォルダー内の NanoServerImageGenerator フォルダーを指定してください。
    
    .NOTES
        This script is only for Windows Server 2016 RTM (10.0.14393).
        This script is tested with Windows Server 2016 Evaluation 10.0.14393.0 JA-JP only.
        Use this at your own risk!
#>

ヘルプ キーワード

ヘルプ キーワードはドット(.)で始まります。

  • .SYNOPSIS
    関数またはスクリプトの簡単な説明。
  • .DESCRIPTION
    関数またはスクリプトの詳細な説明。
  • .PARAMETER <パラメーター名>
    パラメーターの説明。
  • .EXAMPLE
    関数またはスクリプトの使用方法(使用例)。
  • .INPUTS
    入力オブジェクトの .NET Framework 型。
  • .OUTPUTS
    出力オブジェクトの .NET Framework 型。
  • .NOTES
    関数またはスクリプトに関する追加情報。
  • .LINK
    「関連リンク」セクションに表示される内容。

キーワードは他にもありますが、簡単なスクリプトであれば上記以外を使用するシーンは少ないと思います。

スクリプトの説明を、通常のコメントとしてスクリプト内に埋め込むのもよいですが、PowerShell として用意されている方法がありますので、なるべくスクリプトのヘルプ トピックを記述するくせをつけると良さそうです。

Written by kazu

2016/09/29 at 03:24

WMI 経由でレジストリの値を取得する

leave a comment »

Windows Server を管理していると、複数のコンピュータから特定のレジストリキー配下の値を一括で取得したい、と思うことがあります。ただ、Windows Server 2008 などの若干古い OS を対象とした場合、管理対象で PowerShell Remoting が有効になっていなかったり、WMF (Windows Management Framework)のバージョンが古かったりなどで、PowerShell だけでは一括処理が難しい場合があります。

今回は、PowerShell から WMI 経由で複数のリモートコンピューターに接続し、レジストリのサブキー配下の値を再帰的に取得してみます。

前提条件

  • ターゲットコンピューターに WMI で接続できること
  • クライアント(管理側・スクリプト実行側)コンピューター上で PowerShell 3.0 以降が実行できること(2.0 でも動く気がしますが、未検証)

※認証や委任周りで大変面倒なことになるので、今回試している環境はドメイン環境かつ Domain Admins に種族しているアカウントでスクリプトを実行しています。

リモートから複数コンピューターのレジストリ値を取得する

以下のコードは GitHub 上でも参照できます

Param(
    [parameter(Mandatory=$true)]
    [string[]]$ComputerName
)

$HKEY_LOCAL_MACHINE = 2147483650
$ns = "root\default"
$cls = "StdRegProv"
$regkey = "SOFTWARE\Policies\Microsoft"

$result = New-Object System.Collections.ArrayList

$ComputerName | % {
    $c = $_
    $wmi = Get-WmiObject -List $cls -Namespace $ns -ComputerName $c
    $r = $wmi.EnumKey($HKEY_LOCAL_MACHINE, $regkey)
    $r.sNames | % {
        $obj = New-Object -TypeName PSObject
        Add-Member -InputObject $obj -MemberType NoteProperty -Name ComputerName -Value $c
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Name -Value $_
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "SubKey"
        Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $null
        $result.Add($obj) | Out-Null
    }
    $r = $wmi.EnumValues($HKEY_LOCAL_MACHINE, $regkey)
    if($r.ReturnValue -eq 0) {
        for($i = 0; $i -lt $r.sNames.Count; $i++) {
            $obj = New-Object -TypeName PSObject
            Add-Member -InputObject $obj -MemberType NoteProperty -Name ComputerName -Value $c
            Add-Member -InputObject $obj -MemberType NoteProperty -Name Name -Value $r.sNames[$i]
            switch ($r.Types[$i]) {
                1 { # REG_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_SZ"
                    $val = $wmi.GetStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                2 { # REG_EXPAND_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_EXPAND_SZ"
                    $val = $wmi.GetExpandedStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                3 { # REG_BINARY
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_BINARY"
                    $val = $wmi.GetBinaryValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                4 { # REG_DWORD
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_DWORD"
                    $val = $wmi.GetDWORDValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                7 { # REG_MULTI_SZ
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_MULTI_SZ"
                    $val = $wmi.GetMultiStringValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.sValue
                }
                11 { # REG_QWORD
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Type -Value "REG_QWORD"
                    $val = $wmi.GetQWORDValue($HKEY_LOCAL_MACHINE, $regkey, $r.sNames[$i])
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Value -Value $val.uValue
                }
                default { # Invalid Object
                }
            }
            $result.Add($obj) | Out-Null
        }
    }
}

return $result

$regkey に設定したレジストリ サブキー配下を取得しますので、適当に書き換えてください。(本来はパラメーター化すべきですが、今回は面倒だったのでそのままです。)接続先コンピューターは、スクリプトのパラメーター –ComputerName で指定できます。配列(PowerShell Console 上だとカンマ区切り)で渡せば、複数コンピューターを指定できます。

Written by kazu

2016/08/21 at 16:09

SCOM タスクから PowerShell スクリプトを実行する

leave a comment »

System Center 2012 SP1 Operations Manager のタスクとして、PowerShell スクリプトを実行する方法です。

System Center Operations Manager のエージェントタスク

System Center Operations Manager (SCOM)のエージェントタスクは、管理対象(ターゲット)コンピューター上で実行されるスクリプトまたはプログラムです。エージェントタスクを実行すると、ターゲットコンピューター上で処理が実行され、その出力が SCOM コンソールに表示されます。

エージェントタスクは以下の 3 種類(Windows コンピューター向けには 2 種類)です。

  1. コマンドライン タスク
  2. UNIX/Linux シェルコマンド タスク
  3. スクリプト タスク

PowerShell スクリプトを実行したい場合、スクリプト タスクを使用したくなりますが、スクリプト タスクは VBScript および JScript を実行するためのタスクです。PowerShell スクリプトは実行できません。

PowerShell スクリプトを実行する場合は、コマンドライン タスクを使用します。

PowerShell スクリプトを実行するタスクの作成

「ファイルへの完全パス」に powershell.exe のパスを、「パラメーター」にスクリプトファイルの指定を含む、各種引数を設定します。

scom-task-powershell

ここでは、mgr01 の管理共有経由でアクセス可能なスクリプトファイルを実行するスクリプトとして指定しています。-NoLogo で powershell.exe 起動時のロゴ表示を抑制し、-ExecutionPolicy でこのタスクのみ実行ポリシーを Bypass に変更しています。(管理共有上のスクリプトが、外部スクリプトと認識されるため。)

参考

SCOM タスクとして登録する際に、該当のスクリプトのみ実行ポリシーを変更する方法は、以下のポストをご確認ください。

一時的に PowerShell スクリプト実行ポリシーを変更する

with one comment

意外と知らないという方が多かったので、まとめておきます。

PowerShell でスクリプトを実行するには

PowerShell でスクリプトを実行するためには、実行ポリシー(Execution Policy)が適切に設定されている必要があります。実行ポリシーは、ヘルプ トピックの about_Execution_Policies に詳しく書かれています。

Windows PowerShell の実行ポリシーを使用して、Windows PowerShell によって構成ファイルが読み込まれてスクリプトが実行される条件を決定できます。

実行ポリシーは、ローカル コンピューター、現在のユーザー、特定のセッションに設定できます。グループ ポリシー設定を使用して、複数のコンピューターやユーザーに実行ポリシーを設定することもできます。

ローカル コンピューターの実行ポリシーと現在のユーザーの実行ポリシーは、レジストリに格納されます。Windows PowerShell プロファイルに実行ポリシーを設定する必要はありません。特定のセッションの実行ポリシーは、メモリ内にのみ格納され、セッションが閉じられると失われます。

実行ポリシーはユーザーの操作を制限するセキュリティ システムではありません。たとえば、ユーザーはスクリプトを実行できないとき、コマンド ラインでスクリプトの内容を入力すると、容易にポリシーを回避できます。一方で、実行ポリシーを使用して基本的な規則を設定することで、意図せずに違反することを防止することができます。

PowerShell コンソール上でヘルプを表示する場合は、以下を実行します。

Get-Help about_Execution_Policies

現在の設定を取得する場合は、Get-ExecutionPolicy コマンドレットを使用します。

PS C:\> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine    RemoteSigned


PS C:\>

各スコープに対して、現在どのようなポリシーが適用されているか確認できます。

Execution Policy の種類

簡潔に記載すると以下の通りです。詳細は、TechNet を参照してください。

Restricted

  • いかなる場合もスクリプトや外部モジュールの実行は禁止
  • クライアント系 OS および 2012 までのサーバー系 OS の既定

AllSigned

  • 全てのスクリプトに電子署名が必要
  • 電子署名の証明書が信頼されていない場合は、確認メッセージを表示

RemoteSigned

  • 原則 AllSigned と同じだが、ローカル(NTFS Zone.Identifier 的な意味で)のスクリプトについては署名なしで実行可能
  • 2012 R2 以降のサーバ系 OS の既定

Unrestricted

  • 全てのスクリプトを実行可能だが、ローカルのスクリプト以外は警告を表示

Bypass

  • 全てのスクリプトを実行可能で、かつ警告も出さない

スコープ

実行ポリシーはスコープに対して適用されます。スコープごとに異なるポリシーを設定することもできます。その場合、上位のスコープに設定されたポリシーが優先されます。(全て Undefined の場合は、Restricted とみなされます。)

  1. MachinePolicy : グループポリシーのコンピューターポリシーによる設定
  2. UserPolicy : グループポリシーのユーザーポリシーによる設定
  3. Process : ある PowerShell プロセス内(のみ)
  4. CurrentUser : あるコンピューターの、あるユーザーのみ(あるコンピューターの、特定ユーザーのグローバル設定)
  5. LocalMachine : あるコンピューターのみ(あるコンピューターの、全ユーザーのグローバル設定)

1 と 2 はグループポリシーによる設定、3 ~ 5 は、Set-ExecutionPolicy を使用して設定します。

一時的に Execution Policy を変更する

やっと本題です。上記に記載の通り、PowerShell の実行ポリシーはグループポリシーで強制されていない限りプロセス単位で変更が可能です。かつ、Process スコープ(および CurrentUser スコープ)は、管理者でなくともポリシーの設定が可能です。

PowerShell 起動後(開始後)に変更

PowerShell プロセス起動後は、Set-ExecutionPolicy の –Scope パラメーターで Process を指定すると、それ以降のポリシーを変更できます。

PS C:\> Get-Content C:\ps\sample01.ps1
$PSHOME
PS C:\> .\ps\sample01.ps1
.\ps\sample01.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル C:\ps\sample01.ps1 を読み込むこと
ができません。詳細については、「about_Execution_Policies」(http://go.microsoft.com/fwlink/?LinkID=135170) を参照してく
ださい。
発生場所 行:1 文字:1
+ .\ps\sample01.ps1
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
PS C:\> Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned

実行ポリシーの変更
実行ポリシーは、信頼されていないスクリプトからの保護に役立ちます。実行ポリシーを変更すると、about_Execution_Policies
のヘルプ トピック (http://go.microsoft.com/fwlink/?LinkID=135170)
で説明されているセキュリティ上の危険にさらされる可能性があります。実行ポリシーを変更しますか?
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ (既定値は "N"): Y
PS C:\> .\ps\sample01.ps1
C:\Windows\System32\WindowsPowerShell\v1.0
PS C:\>

PowerShell 起動時に変更

PowerShell 起動時に、その(これから起動する)PowerShell プロセスのみポリシーを変更したい場合は、powershell.exe のオプションとしてポリシーを渡します。

C:\>powershell.exe -NoLogo -ExecutionPolicy RemoteSigned -File .\ps\sample01.ps1
C:\Windows\System32\WindowsPowerShell\v1.0

C:\>

まとめ

  • 特定のバッチやタスクのみ、一時的な対応などでスクリプトの実行が必要な場合は、一時的に(該当プロセスのみ)実行ポリシーを変更してスクリプトを実行できます。
  • 管理者としてユーザーによる一時的な実行ポリシーの変更が許容できない場合は、グループポリシーでのポリシー配布が必要です。

ネットワークの場所を「プライベート」に変更する

leave a comment »

いつも忘れるのでメモ。

PS C:\> Get-NetConnectionProfile | ? NetworkCategory -eq "Public" | Set-NetConnectionProfile -NetworkCategory "Private"

 

管理者権限に昇格して実行することを忘れずに。

ドメインネットワークは良いとして、ストレージ通信用やハートビート用のネットワークは忘れずに Private へ変更する。

Written by kazu

2015/03/12 at 13:00

ログインユーザーの一覧を表示するワンライナー(PowerShell)

leave a comment »

現在ホストにログインしているアカウント(ドメイン名およびユーザー名)と、ログインタイプ(ログイン方法)を表示するワンライナー。ワンライナーにする必要は全く無いのですが。(コピペしやすい、くらいのメリットしかない。)

Get-CimInstance -ClassName Win32_LoggedOnUser | %{ $o = New-Object PSObject | select Domain,User,LogonType; $o.Domain, $o.User, $o.LogonType = $_.Antecedent.Domain, $_.Antecedent.Name, @{ 0 = "System Reserved (System Account)"; 2 = "Interactive"; 3 = "Network"; 4 = "Batch"; 5 = "Service"; 6 = "Proxy"; 7 = "Unlock"; 8 = "NetworkCleartext"; 9 = "NewCredentials"; 10 = "RemoteInteractive"; 11 = "CachedInteractive"; 12 = "CachedRemoteInteractive"; 13 = "CachedUnlock" }[[int]((Get-CimInstance -ClassName Win32_LogonSession -Filter "LogonId=$($_.Dependent.LogonId)").LogonType)]; $o } | ft * -AutoSize

Domain    User            LogonType
------    ----            ---------
SVVNTP201 SYSTEM          System Reserved (System Account)
SVVNTP201 LOCAL SERVICE   Service
SVVNTP201 NETWORK SERVICE Service
CONTOSO   test01          RemoteInteractive
CONTOSO   test01          Network
CONTOSO   test02          Interactive
CONTOSO   test03          Network
CONTOSO   test03          Network
SVVNTP201 Administrator   RemoteInteractive
SVVNTP201 Administrator   Network
SVVNTP201 Administrator   Network
SVVNTP201 Administrator   Network
SVVNTP201 ANONYMOUS LOGON Network
SVVNTP201 DWM-1           Interactive
SVVNTP201 DWM-2           Interactive
SVVNTP201 DWM-3           Interactive

Interactive は、ローカルから GUI でログインした場合や「別のユーザーとして実行」で cmd.exe 等を起動した場合、RemoteInteractive は RDP(Remote Desktop Service / Terminal Service)、Network はネットワーク経由でのログオン(WinRM を使用した PowerShell Remoting を含む)。

Written by kazu

2015/02/09 at 21:13

PowerShell の $a –eq $b と $b –eq $a は同じ結果とは限らない

leave a comment »

一言で言うと、タイトルの通りなのですが。他の言語を使用している方は、(普段使っている言語によっては)違和感があるかもしれません。

もともと、PowerShell の –eq 演算子は、配列(リスト)に適用したときと値型(Scalar Value)に適用したときで動作が異なります。が、値型同士の比較であっても、結果が異なる場合があります。

PS C:\> $true -eq 10
True
PS C:\> 10 -eq $true
False

$true は bool 型、10 は int 型で、ともに値型ですが、上記の通り異なる結果になります。これは、Boolean や Null ($null)と数値を比較した場合、右側のオペランド(被演算子)が左オペランドの型へ暗黙的にキャストされるためです。ですので、上の例は、以下と同等になります。

PS C:\> [bool]10
True
PS C:\> $true -eq [bool]10
True
PS C:\> [int]$true
1
PS C:\> 10 -eq [int]$true
False

ちなみに、通常の数値型(int や double 等)を –eq 演算子で比較した場合は、左オペランドの型へキャストされるのではなく、左右のオペランドのうち精度の高い型や範囲の広い型へキャストされます。(つまり、必ず左オペランドの型へキャストされるわけではありません。)

PS C:\> 10 -eq 10.1
False
PS C:\> 10.1 -eq 10
False
参考

Written by kazu

2015/01/31 at 11:30