about_Classes_and_DSC
簡単な説明
クラスを使用して、Desired State Configuration (DSC) を使用して PowerShell で開発する方法について説明します。
長い説明
Windows PowerShell 5.0 以降では、他のオブジェクト指向プログラミング言語に似た正式な構文とセマンティクスを使用して、クラスやその他のユーザー定義型を定義する言語が追加されました。 目標は、開発者と IT プロフェッショナルが、より広範なユース ケースに PowerShell を採用し、DSC リソースなどの PowerShell 成果物の開発を簡略化し、管理サーフェイスのカバレッジを高速化できるようにすることです。
サポートされているシナリオ
次のシナリオがサポートされています。
- PowerShell 言語を使用して、DSC リソースとそれに関連付けられている型を定義します。
- クラス、プロパティ、メソッド、継承などの使い慣れたオブジェクト指向プログラミング コンストラクトを使用して、PowerShell でカスタム型を定義します。
- PowerShell 言語を使用して型をデバッグします。
- 正式なメカニズムを使用して、適切なレベルで例外を生成して処理します。
クラスを使用して DSC リソースを定義する
構文の変更とは別に、クラス定義 DSC リソースとコマンドレット DSC リソース プロバイダーの主な違いは次のとおりです。
- 管理オブジェクト形式 (MOF) ファイルは必要ありません。
- モジュール フォルダー内の DSCResource サブフォルダーは必要ありません。
- PowerShell モジュール ファイルには、複数の DSC リソース クラスを含めることができます。
クラス定義 DSC リソース プロバイダーを作成する
次の例は、モジュール MyDSCResource.psm1 として保存されるクラス定義 DSC リソース プロバイダーです。 クラス定義 DSC リソース プロバイダーには、常にキー プロパティを含める必要があります。
enum Ensure
{
Absent
Present
}
<#
This resource manages the file in a specific path.
[DscResource()] indicates the class is a DSC resource
#>
[DscResource()]
class FileResource
{
<#
This property is the fully qualified path to the file that is
expected to be present or absent.
The [DscProperty(Key)] attribute indicates the property is a
key and its value uniquely identifies a resource instance.
Defining this attribute also means the property is required
and DSC will ensure a value is set before calling the resource.
A DSC resource must define at least one key property.
#>
[DscProperty(Key)]
[string]$Path
<#
This property indicates if the settings should be present or absent
on the system. For present, the resource ensures the file pointed
to by $Path exists. For absent, it ensures the file point to by
$Path does not exist.
The [DscProperty(Mandatory)] attribute indicates the property is
required and DSC will guarantee it is set.
If Mandatory is not specified or if it is defined as
Mandatory=$false, the value is not guaranteed to be set when DSC
calls the resource. This is appropriate for optional properties.
#>
[DscProperty(Mandatory)]
[Ensure] $Ensure
<#
This property defines the fully qualified path to a file that will
be placed on the system if $Ensure = Present and $Path does not
exist.
NOTE: This property is required because [DscProperty(Mandatory)] is
set.
#>
[DscProperty(Mandatory)]
[string] $SourcePath
<#
This property reports the file's create timestamp.
[DscProperty(NotConfigurable)] attribute indicates the property is
not configurable in DSC configuration. Properties marked this way
are populated by the Get() method to report additional details
about the resource when it is present.
#>
[DscProperty(NotConfigurable)]
[Nullable[datetime]] $CreationTime
<#
This method is equivalent of the Set-TargetResource script function.
It sets the resource to the desired state.
#>
[void] Set()
{
$fileExists = $this.TestFilePath($this.Path)
if($this.ensure -eq [Ensure]::Present)
{
if(-not $fileExists)
{
$this.CopyFile()
}
}
else
{
if($fileExists)
{
Write-Verbose -Message "Deleting the file $($this.Path)"
Remove-Item -LiteralPath $this.Path -Force
}
}
}
<#
This method is equivalent of the Test-TargetResource script
function. It should return True or False, showing whether the
resource is in a desired state.
#>
[bool] Test()
{
$present = $this.TestFilePath($this.Path)
if($this.Ensure -eq [Ensure]::Present)
{
return $present
}
else
{
return -not $present
}
}
<#
This method is equivalent of the Get-TargetResource script function.
The implementation should use the keys to find appropriate
resources. This method returns an instance of this class with the
updated key properties.
#>
[FileResource] Get()
{
$present = $this.TestFilePath($this.Path)
if ($present)
{
$file = Get-ChildItem -LiteralPath $this.Path
$this.CreationTime = $file.CreationTime
$this.Ensure = [Ensure]::Present
}
else
{
$this.CreationTime = $null
$this.Ensure = [Ensure]::Absent
}
return $this
}
<#
Helper method to check if the file exists and it is correct file
#>
[bool] TestFilePath([string] $location)
{
$present = $true
$item = Get-ChildItem -LiteralPath $location -ea Ignore
if ($null -eq $item)
{
$present = $false
}
elseif( $item.PSProvider.Name -ne "FileSystem")
{
throw "Path $($location) is not a file path."
}
elseif($item.PSIsContainer)
{
throw "Path $($location) is a directory path."
}
return $present
}
<#
Helper method to copy file from source to path
#>
[void] CopyFile()
{
if(-not $this.TestFilePath($this.SourcePath))
{
throw "SourcePath $($this.SourcePath) is not found."
}
[System.IO.FileInfo]
$destFileInfo = new-object System.IO.FileInfo($this.Path)
if (-not $destFileInfo.Directory.Exists)
{
$FullName = $destFileInfo.Directory.FullName
$Message = "Creating directory $FullName"
Write-Verbose -Message $Message
#use CreateDirectory instead of New-Item to avoid code
# to handle the non-terminating error
[System.IO.Directory]::CreateDirectory($FullName)
}
if(Test-Path -LiteralPath $this.Path -PathType Container)
{
throw "Path $($this.Path) is a directory path"
}
Write-Verbose -Message "Copying $this.SourcePath to $this.Path"
#DSC engine catches and reports any error that occurs
Copy-Item -Path $this.SourcePath -Destination $this.Path -Force
}
}
モジュール マニフェストを作成する
クラス定義 DSC リソース プロバイダーを作成し、それをモジュールとして保存したら、モジュールのモジュール マニフェストを作成します。 クラス ベースのリソースを DSC エンジンで使用できるようにするには、リソースをエクスポートするようにモジュールに指示する DscResourcesToExport
ステートメントをマニフェスト ファイルに含める必要があります。 この例では、次のモジュール マニフェストは MyDscResource.psd1 として保存されます。
@{
# Script module or binary module file associated with this manifest.
RootModule = 'MyDscResource.psm1'
DscResourcesToExport = 'FileResource'
# Version number of this module.
ModuleVersion = '1.0'
# ID used to uniquely identify this module
GUID = '81624038-5e71-40f8-8905-b1a87afe22d7'
# Author of this module
Author = 'Microsoft Corporation'
# Company or vendor of this module
CompanyName = 'Microsoft Corporation'
# Copyright statement for this module
Copyright = '(c) 2014 Microsoft. All rights reserved.'
# Description of the functionality provided by this module
# Description = ''
# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.0'
# Name of the PowerShell host required by this module
# PowerShellHostName = ''
}
DSC リソース プロバイダーをデプロイする
$pshome\Modules
または $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
に MyDscResource フォルダーを作成して、新しい DSC リソース プロバイダーをデプロイします。
DSCResource サブフォルダーを作成する必要はありません。 モジュール マニフェスト ファイルとモジュール マニフェスト ファイル (MyDscResource.psm1 と MyDscResource.psd1) を MyDscResource フォルダーにコピーします。
この時点から、任意の DSC リソースと同様に、構成スクリプトを作成して実行します。
DSC 構成スクリプトを作成する
前述のように、フォルダー構造にクラス ファイルとマニフェスト ファイルを保存した後、新しいリソースを使用する構成を作成できます。 次の構成では、MyDSCResource モジュールを参照しています。 構成をスクリプトとして保存 MyResource.ps1。
DSC 構成を実行する方法については、「Windows PowerShell Desired State Configuration Overview」を参照してください。
構成を実行する前に、C:\test.txt
を作成します。 構成では、ファイルが c:\test\test.txt
に存在するかどうかを確認します。 ファイルが存在しない場合、構成はファイルを C:\test.txt
からコピーします。
Configuration Test
{
Import-DSCResource -ModuleName MyDscResource
FileResource file
{
Path = "C:\test\test.txt"
SourcePath = "C:\test.txt"
Ensure = "Present"
}
}
Test
Start-DscConfiguration -Wait -Force Test
DSC 構成スクリプトと同様に、このスクリプトを実行します。 構成を開始するには、管理者特権の PowerShell コンソールで次を実行します。
PS C:\test> .\MyResource.ps1
PowerShell クラスでの継承
PowerShell クラスの基底クラスを宣言する
次の例に示すように、PowerShell クラスを別の PowerShell クラスの基本型として宣言できます。この例では、
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
PowerShell クラスの実装済みインターフェイスを宣言する
基本型の後に実装されたインターフェイスを宣言することも、基本型が指定されていない場合はコロン (:
) の直後に宣言することもできます。 コンマを使用して、すべての型名を区切ります。 これは C# 構文に似ています。
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
基底クラスのコンストラクターを呼び出す
サブクラスから基底クラス コンストラクターを呼び出すには、次の例に示すように、base
キーワードを追加します。
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
基底クラスに既定のコンストラクター (パラメーターなし) がある場合は、次のように明示的なコンストラクター呼び出しを省略できます。
class C : B
{
C([int]$c) {}
}
基底クラス メソッドを呼び出す
サブクラス内の既存のメソッドをオーバーライドできます。 オーバーライドを行うには、同じ名前とシグネチャを使用してメソッドを宣言します。
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
オーバーライドされた実装から基底クラス メソッドを呼び出すには、呼び出し時に基底クラス ([baseclass]$this)
にキャストします。
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
すべての PowerShell メソッドは仮想です。 オーバーライドの場合と同じ構文を使用して、サブクラス内の非仮想 .NET メソッドを非表示にすることができます。同じ名前とシグネチャを持つメソッドを宣言します。
class MyIntList : system.collections.generic.list[int]
{
# Add is final in system.collections.generic.list
[void] Add([int]$arg)
{
([system.collections.generic.list[int]]$this).Add($arg * 2)
}
}
$list = [MyIntList]::new()
$list.Add(100)
$list[0] # return 200
クラス継承に関する現在の制限事項
クラス継承の制限は、PowerShell でインターフェイスを宣言する構文が存在しないということです。
PowerShell でのカスタム型の定義
Windows PowerShell 5.0 では、いくつかの言語要素が導入されました。
Class キーワード
新しいクラスを定義します。
class
キーワードは、真の .NET Framework 型です。
クラス メンバーはパブリックです。
class MyClass
{
}
Enum キーワードと列挙型
enum
キーワードのサポートが追加され、重大な変更です。 現在、enum
区切り記号は改行です。
enum
を既に使用しているユーザーの回避策は、単語の前にアンパサンド (&
) を挿入することです。 現在の制限事項: 列挙子自体を定義することはできませんが、次の例に示すように、別の enum
の観点から enum
を初期化できます。
基本型は現在指定できません。 基本型は常に [int] です。
enum Color2
{
Yellow = [Color]::Blue
}
列挙子の値は、解析時間定数である必要があります。 列挙子の値を、呼び出されたコマンドの結果に設定することはできません。
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
Enum
では、次の例に示すように算術演算がサポートされています。
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
Hidden キーワード
Windows PowerShell 5.0 で導入された hidden
キーワードは、クラス メンバーを既定の Get-Member
結果から隠します。 次の行に示すように、非表示のプロパティを指定します。
hidden [type] $classmember = <value>
非表示のメンバーを定義するクラスで入力候補が発生しない限り、非表示のメンバーはタブ補完または IntelliSense を使用して表示されません。
C# コードが PowerShell 内で同じセマンティクスを持てるように、System.Management.Automation.HiddenAttribute
詳細については、[about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden) を参照してください。
Import-DscResource
Import-DscResource
が真の動的キーワードになりました。 PowerShell は、指定されたモジュールのルート モジュールを解析し、DscResource 属性を含むクラスを検索します。
プロパティ
ModuleInfo
に新しいフィールド ImplementingAssembly
が追加されました。 スクリプトでクラスが定義されている場合、またはバイナリ モジュール用に読み込まれたアセンブリ ImplementingAssembly
スクリプト モジュール用に作成された動的アセンブリに設定されている場合。 ModuleType = Manifest の場合は設定されません。
ImplementingAssembly
フィールドのリフレクションでは、モジュール内のリソースが検出されます。 つまり、PowerShell またはその他のマネージド言語で記述されたリソースを検出できます。
初期化子を持つフィールド。
[int] $i = 5
Static はサポートされており、型制約と同様に属性のように機能するため、任意の順序で指定できます。
static [int] $count = 0
型は省略可能です。
$s = "hello"
すべてのメンバーはパブリックです。 プロパティには、改行またはセミコロンが必要です。 オブジェクト型が指定されていない場合、プロパティの型は Object
コンストラクターとインスタンス化
PowerShell クラスには、クラスと同じ名前を持つコンストラクターを含めることができます。 コンストラクターはオーバーロードできます。 静的コンストラクターがサポートされています。
初期化式を持つプロパティは、コンストラクターでコードを実行する前に初期化されます。 静的プロパティは静的コンストラクターの本体の前に初期化され、インスタンス プロパティは非静的コンストラクターの本体の前に初期化されます。 現時点では、C# 構文など、別のコンストラクターからコンストラクターを呼び出すための構文はありません:": this()")
。 回避策は、一般的な Init メソッドを定義することです。
クラスをインスタンス化する方法を次に示します。
既定のコンストラクターを使用したインスタンス化。 このリリースでは、
New-Object
はサポートされていないことに注意してください。$a = [MyClass]::new()
パラメーターを使用してコンストラクターを呼び出す。
$b = [MyClass]::new(42)
複数のパラメーターを持つコンストラクターに配列を渡す
$c = [MyClass]::new(@(42,43,44), "Hello")
このリリースでは、型名は字句的にのみ表示されます。つまり、クラスを定義するモジュールまたはスクリプトの外部には表示されません。 関数は、PowerShell で定義されているクラスのインスタンスを返すことができます。また、インスタンスはモジュールまたはスクリプトの外部で適切に機能します。
Get-Member
Static パラメーターにはコンストラクターが一覧表示されるため、他のメソッドと同様にオーバーロードを表示できます。 この構文のパフォーマンスも、New-Object
よりもかなり高速です。
新しい [hashtable]::new()
Get-Member
を使用して、または次の例に示すように、コンストラクターのオーバーロードを確認できるようになりました。
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
メソッド
PowerShell クラス メソッドは、終了ブロックのみを持つ ScriptBlock として実装されます。 すべてのメソッドはパブリックです。 DoSomething
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
メソッドの呼び出し
オーバーロードされたメソッドがサポートされています。 オーバーロードされたメソッドの名前は既存のメソッドと同じですが、指定された値によって区別されます。
$b = [MyClass]::new()
$b.DoSomething(42)
援用
メソッド呼び出し
属性
DscResource
、DscResourceKey
、DscResourceMandatory
の 3 つの新しい属性が追加されました。
戻り値の型
戻り値の型はコントラクトです。 戻り値は、想定される型に変換されます。 戻り値の型が指定されていない場合、戻り値の型は void です。 オブジェクトのストリーミングはなく、意図的または誤ってパイプラインにオブジェクトを書き込むことはできません。
変数の字句スコープ
このリリースでの字句スコープの動作の例を次に示します。
$d = 42 # Script scope
function bar
{
$d = 0 # Function scope
[MyClass]::DoSomething()
}
class MyClass
{
static [object] DoSomething()
{
return $d # error, not found dynamically
return $script:d # no error
$d = $script:d
return $d # no error, found lexically
}
}
$v = bar
$v -eq $d # true
例: カスタム クラスを作成する
次の例では、HTML 動的スタイルシート言語 (DSL) を実装するための新しいカスタム クラスをいくつか作成します。 この例では、要素クラスの一部として特定の要素型を作成するヘルパー関数を追加します。これは、モジュールの範囲外では型を使用できないためです。
# Classes that define the structure of the document
#
class Html
{
[string] $docType
[HtmlHead] $Head
[Element[]] $Body
[string] Render()
{
$text = "<html>`n<head>`n"
$text += $Head
$text += "`n</head>`n<body>`n"
$text += $Body -join "`n" # Render all of the body elements
$text += "</body>`n</html>"
return $text
}
[string] ToString() { return $this.Render() }
}
class HtmlHead
{
$Title
$Base
$Link
$Style
$Meta
$Script
[string] Render() { return "<title>$Title</title>" }
[string] ToString() { return $this.Render() }
}
class Element
{
[string] $Tag
[string] $Text
[hashtable] $Attributes
[string] Render() {
$attributesText= ""
if ($Attributes)
{
foreach ($attr in $Attributes.Keys)
{
$attributesText = " $attr=`"$($Attributes[$attr])`""
}
}
return "<${tag}${attributesText}>$text</$tag>`n"
}
[string] ToString() { return $this.Render() }
}
#
# Helper functions for creating specific element types on top of the classes.
# These are required because types aren't visible outside of the module.
#
function H1 {[Element] @{Tag = "H1"; Text = $args.foreach{$_} -join " "}}
function H2 {[Element] @{Tag = "H2"; Text = $args.foreach{$_} -join " "}}
function H3 {[Element] @{Tag = "H3"; Text = $args.foreach{$_} -join " "}}
function P {[Element] @{Tag = "P" ; Text = $args.foreach{$_} -join " "}}
function B {[Element] @{Tag = "B" ; Text = $args.foreach{$_} -join " "}}
function I {[Element] @{Tag = "I" ; Text = $args.foreach{$_} -join " "}}
function HREF
{
param (
$Name,
$Link
)
return [Element] @{
Tag = "A"
Attributes = @{ HREF = $link }
Text = $name
}
}
function Table
{
param (
[Parameter(Mandatory)]
[object[]]
$Data,
[Parameter()]
[string[]]
$Properties = "*",
[Parameter()]
[hashtable]
$Attributes = @{ border=2; cellpadding=2; cellspacing=2 }
)
$bodyText = ""
# Add the header tags
$bodyText += $Properties.foreach{TH $_}
# Add the rows
$bodyText += foreach ($row in $Data)
{
TR (-join $Properties.Foreach{ TD ($row.$_) } )
}
$table = [Element] @{
Tag = "Table"
Attributes = $Attributes
Text = $bodyText
}
$table
}
function TH {([Element] @{Tag="TH"; Text=$args.foreach{$_} -join " "})}
function TR {([Element] @{Tag="TR"; Text=$args.foreach{$_} -join " "})}
function TD {([Element] @{Tag="TD"; Text=$args.foreach{$_} -join " "})}
function Style
{
return [Element] @{
Tag = "style"
Text = "$args"
}
}
# Takes a hash table, casts it to and HTML document
# and then returns the resulting type.
#
function Html ([HTML] $doc) { return $doc }
関連項目
カスタム PowerShell Desired State Configuration Resources をビルドする