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 リソース プロバイダーをデプロイする

に MyDscResource フォルダーを作成して、新しい DSC リソース プロバイダーを $pshome\Modules デプロイします $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules

DSCResource サブフォルダーを作成する必要はありません。 モジュールとモジュールのマニフェスト ファイル (MyDscResource.psm1 と MyDscResource.psd1) を MyDscResource フォルダーにコピーします。

これ以降、他の DSC リソースの場合と同様に、構成スクリプトを作成して実行します。

DSC 構成スクリプトを作成する

既に説明したように、クラスとマニフェスト ファイルをフォルダー構造で保存した後で、新しいリソースを使用する構成を作成できます。 次の構成では、MyDSCResource モジュールを参照しています。 構成をスクリプトとして保存MyResource.ps1。

DSC 構成を実行する方法については、「Windows PowerShell Desired State Configurationの概要」を参照してください。

構成を実行する前に作成 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 クラスの基本型として宣言できます。この例では、 フルーツApple の基本型です。

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 true の.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 属性を含むクラスを探して、指定されたモジュールのルート モジュールを解析します。

プロパティ

に新しいフィールド ImplementingAssemblyが追加 ModuleInfoされました。 スクリプトでクラスが定義されている場合、またはバイナリ モジュール用に読み込まれたアセンブリが、スクリプト モジュール 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 で定義されたクラスのインスタンスを返すことができます。また、インスタンスはモジュールまたはスクリプトの外部で適切に機能します。

Static パラメーターにはGet-Memberコンストラクターが一覧表示されるため、他のメソッドと同様にオーバーロードを表示できます。 また、この構文のパフォーマンスは、New-Object より大幅に高速です。

次の例に示すように、new という名前の擬似静的メソッドは .NET 型で機能します。 [hashtable]::new()

Get-Member を使って、またはこの例で示すようにして、コンストラクターのオーバーロードを確認できるようになりました。

[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)

メソッド

PowerShell のクラス メソッドは、end ブロックのみを持つ ScriptBlock として実装されます。 すべてのメソッドはパブリックです。 DoSomething という名前のメソッドを定義する例を次に示します。

class MyClass
{
    DoSomething($x)
    {
        $this._doSomething($x)       # method syntax
    }
    private _doSomething($a) {}
}

メソッドの呼び出し

オーバーロードされたメソッドがサポートされています。 オーバーロードされたメソッドの名前は既存のメソッドと同じですが、指定された値によって区別されます。

$b = [MyClass]::new()
$b.DoSomething(42)

呼び出し

メソッドの呼び出しを参照してください。

属性

次の 3 つの新しい属性が追加されました。 DscResourceDscResourceKeyDscResourceMandatory

戻り値の型

戻り値の型はコントラクトです。 戻り値は、予期された型に変換されます。 戻り値の型が指定されていない場合、戻り値の型は 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 }

こちらもご覧ください

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

カスタム PowerShell Desired State Configuration リソースをビルドする