次の方法で共有


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 クラスの基本型として宣言できます。この例では、フルーツ は 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 キーワードは、真の .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よりもかなり高速です。

新しい という名前の擬似静的メソッドは、次の例に示すように.NET 型で動作します。 [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)

援用

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

属性

DscResourceDscResourceKeyDscResourceMandatoryの 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 }

関連項目

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

カスタム PowerShell Desired State Configuration Resources をビルドする