次の方法で共有


PowerShell クラスを使用したカスタム DSC リソースの記述

適用先:Windows PowerShell 5.0

Windows PowerShell 5.0 の PowerShell クラスの導入により、クラスを作成して DSC リソースを定義できるようになりました。 クラスでは、スキーマとリソースの実装の両方を定義するため、MOF ファイルを別途作成する必要がありません。 DSCResources フォルダーが必要ないため、クラスベースのリソースのフォルダー構造は単純になりました。

クラス ベースの DSC リソースでは、スキーマはクラスのプロパティとして定義されます。このプロパティは、プロパティの種類を指定するために属性で変更できます。 リソースは、Get()Set() 、および Test() メソッド (スクリプト リソースの Get-TargetResourceSet-TargetResource、および Test-TargetResource 関数に相当します) によって実装されます。

この記事では、指定したパス内のファイルを管理する NewFile という名前の単純なリソースを作成します。

DSC リソースの詳細については、「カスタム Windows PowerShell Desired State Configuration のビルド」をご覧ください。

Note

クラスベースのリソースでは、汎用コレクションはサポートされていません。

クラス リソースのフォルダー構造

PowerShell クラスを使用して DSC カスタム リソースを実装するには、次のフォルダー構造を作成します。 クラスは MyDscResource.psm1 で定義し、モジュール マニフェストは MyDscResource.psd1 で定義します。

$env:ProgramFiles\WindowsPowerShell\Modules (folder)
    |- MyDscResource (folder)
        MyDscResource.psm1
        MyDscResource.psd1

クラスの作成

PowerShell クラスを作成するには、class キーワードを使用します。 クラスを DSC リソースとして指定するには、DscResource() 属性を使用します。 クラスの名前は、DSC リソースの名前です。

[DscResource()]
class NewFile {
}

プロパティの宣言

DSC リソースのスキーマは、クラスのプロパティとして定義します。 3 つのプロパティを次のように宣言します。

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

[DscProperty(NotConfigurable)]
[MyDscResourceReason[]] $Reasons

属性によってプロパティが変更されることに注意してください。 属性の意味は次のとおりです。

  • DscProperty(Key) :このプロパティは必須です。 プロパティはキーです。 キーとしてマークされたすべてのプロパティの値を組み合わせて、構成内のリソース インスタンスを一意に識別する必要があります。
  • DscProperty(Mandatory) :このプロパティは必須です。
  • DscProperty(NotConfigurable) :このプロパティは読み取り専用です。 この属性でマークされたプロパティは、構成で設定できませんが、Get() メソッド (存在する場合) によって設定されます。
  • DscProperty() :このプロパティは構成可能ですが、必須ではありません。

$Path プロパティと $SourcePath プロパティは、両方とも文字列です。 $CreationTime は、DateTime プロパティです。 $Ensure プロパティは、次のように定義された列挙型です。

enum Ensure
{
    Absent
    Present
}

埋め込みクラス

リソース内で使用できる定義済みプロパティと共に新しい型を含める場合、前述のようにプロパティ型でクラスを作成します。

class MyDscResourceReason {
    [DscProperty()]
    [string] $Code

    [DscProperty()]
    [string] $Phrase
}

Note

クラスは MyDscResourceReason 、モジュールの名前をプレフィックスとして使用して、ここで宣言されています。 埋め込みクラスには任意の名前を付けることができますが、2 つ以上のモジュールで同じ名前のクラスが定義され、両方とも構成で使用されている場合、PowerShell は例外を発生させます。

DSC で名前の競合によって発生する例外を回避するには、埋め込みクラスの名前の前にモジュール名を付けます。 埋め込みクラスの名前が競合する可能性が既に低い場合は、プレフィックスなしで使用できます。

DSC リソースが Azure Automanage のマシン構成機能で使用するように設計されている場合は、 Reasons プロパティ用に作成した埋め込みクラスの名前の前に必ずプレフィックスを付けます。

パブリック関数とプライベート関数

同じモジュール ファイル内に PowerShell 関数を作成し、その関数を DSC クラス リソースのメソッド内で使用できます。 関数はパブリックとして宣言する必要がありますが、これらのパブリック関数内のスクリプト ブロックはプライベートな関数を呼び出すことができます。 唯一の違いは、モジュール マニフェストの FunctionsToExport プロパティに一覧表示されるかどうかです。

<#
   Public Functions
#>

function Get-File {
    param(
        [ensure]$ensure,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    $fileContent        = [MyDscResourceReason]::new()
    $fileContent.code   = 'file:file:content'

    $filePresent        = [MyDscResourceReason]::new()
    $filePresent.code   = 'file:file:path'

    $ensureReturn = 'Absent'

    $fileExists = Test-path $path -ErrorAction SilentlyContinue

    if ($true -eq $fileExists) {
        $filePresent.phrase     = "The file was expected to be: $ensure`nThe file exists at path: $path"

        $existingFileContent    = Get-Content $path -Raw
        if ([string]::IsNullOrEmpty($existingFileContent)) {
            $existingFileContent = ''
        }

        if ($false -eq ([string]::IsNullOrEmpty($content))) {
            $content = $content | ConvertTo-SpecialChars
        }

        $fileContent.phrase     = "The file was expected to contain: $content`nThe file contained: $existingFileContent"

        if ($content -eq $existingFileContent) {
            $ensureReturn = 'Present'
        }
    }
    else {
        $filePresent.phrase     = "The file was expected to be: $ensure`nThe file does not exist at path: $path"
        $path = 'file not found'
    }

    return @{
        ensure  = $ensureReturn
        path    = $path
        content = $existingFileContent
        Reasons = @($filePresent,$fileContent)
    }
}

function Set-File {
    param(
        [ensure]$ensure = "Present",

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    Remove-Item $path -Force -ErrorAction SilentlyContinue
    if ($ensure -eq "Present") {
        New-Item $path -ItemType File -Force
        if ([ValidateNotNullOrEmpty()]$content) {
            $content | ConvertTo-SpecialChars | Set-Content $path -NoNewline -Force
        }
    }
}

function Test-File {
    param(
        [ensure]$ensure = "Present",

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    $test = $false
    $get = Get-File @PSBoundParameters

    if ($get.ensure -eq $ensure) {
        $test = $true
    }
    return $test
}

<#
   Private Functions
#>

function ConvertTo-SpecialChars {
    param(
        [parameter(Mandatory = $true,ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string]$string
    )
    $specialChars = @{
        '`n' = "`n"
        '\\n' = "`n"
        '`r' = "`r"
        '\\r' = "`r"
        '`t' = "`t"
        '\\t' = "`t"
    }
    foreach ($char in $specialChars.Keys) {
        $string = $string -replace ($char,$specialChars[$char])
    }
    return $string
}

メソッドの実装

Get()Set() 、および Test() メソッドは、スクリプト リソースの Get-TargetResourceSet-TargetResource、および Test-TargetResource 関数に似ています。

ベスト プラクティスとして、クラス実装内のコードの量を最小限に抑えてください。 代わりに、コードの大部分をモジュール内のパブリック関数に移動します。これにより、個別にテストできます。

<#
    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.
#>
[NewFile] Get() {
    $get = Get-File -ensure $this.ensure -path $this.path -content $this.content
    return $get
}

<#
    This method is equivalent of the Set-TargetResource script function.
    It sets the resource to the desired state.
#>
[void] Set() {
    $set = Set-File -ensure $this.ensure -path $this.path -content $this.content
}

<#
    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() {
    $test = Test-File -ensure $this.ensure -path $this.path -content $this.content
    return $test
}

完全なファイル

完全なクラス ファイルは次のとおりです。

enum ensure {
    Absent
    Present
}

<#
    This class is used within the DSC Resource to standardize how data
    is returned about the compliance details of the machine. Note that
    the class name is prefixed with the module name - this helps prevent
    errors raised when multiple modules with DSC Resources define the
    Reasons property for reporting when they're out-of-state.
#>
class MyDscResourceReason {
    [DscProperty()]
    [string] $Code

    [DscProperty()]
    [string] $Phrase
}

<#
   Public Functions
#>

function Get-File {
    param(
        [ensure]$ensure,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    $fileContent        = [MyDscResourceReason]::new()
    $fileContent.code   = 'file:file:content'

    $filePresent        = [MyDscResourceReason]::new()
    $filePresent.code   = 'file:file:path'

    $ensureReturn = 'Absent'

    $fileExists = Test-path $path -ErrorAction SilentlyContinue

    if ($true -eq $fileExists) {
        $filePresent.phrase     = "The file was expected to be: $ensure`nThe file exists at path: $path"

        $existingFileContent    = Get-Content $path -Raw
        if ([string]::IsNullOrEmpty($existingFileContent)) {
            $existingFileContent = ''
        }

        if ($false -eq ([string]::IsNullOrEmpty($content))) {
            $content = $content | ConvertTo-SpecialChars
        }

        $fileContent.phrase     = "The file was expected to contain: $content`nThe file contained: $existingFileContent"

        if ($content -eq $existingFileContent) {
            $ensureReturn = 'Present'
        }
    }
    else {
        $filePresent.phrase     = "The file was expected to be: $ensure`nThe file does not exist at path: $path"
        $path = 'file not found'
    }

    return @{
        ensure  = $ensureReturn
        path    = $path
        content = $existingFileContent
        Reasons = @($filePresent,$fileContent)
    }
}

function Set-File {
    param(
        [ensure]$ensure = "Present",

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    Remove-Item $path -Force -ErrorAction SilentlyContinue
    if ($ensure -eq "Present") {
        New-Item $path -ItemType File -Force
        if ([ValidateNotNullOrEmpty()]$content) {
            $content | ConvertTo-SpecialChars | Set-Content $path -NoNewline -Force
        }
    }
}

function Test-File {
    param(
        [ensure]$ensure = "Present",

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$path,

        [String]$content
    )
    $test = $false
    $get = Get-File @PSBoundParameters

    if ($get.ensure -eq $ensure) {
        $test = $true
    }
    return $test
}

<#
   Private Functions
#>

function ConvertTo-SpecialChars {
    param(
        [parameter(Mandatory = $true,ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string]$string
    )
    $specialChars = @{
        '`n' = "`n"
        '\\n' = "`n"
        '`r' = "`r"
        '\\r' = "`r"
        '`t' = "`t"
        '\\t' = "`t"
    }
    foreach ($char in $specialChars.Keys) {
        $string = $string -replace ($char,$specialChars[$char])
    }
    return $string
}

<#
    This resource manages the file in a specific path.
    [DscResource()] indicates the class is a DSC resource
#>

[DscResource()]
class NewFile {

    <#
        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 is optional. When provided, the content of the file
        will be overwridden by this value.
    #>
    [DscProperty()]
    [string] $content

    <#
        This property reports the reasons the machine is or is not compliant.

        [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)]
    [MyDscResourceReason[]] $Reasons

    <#
        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.
    #>
    [NewFile] Get() {
        $get = Get-File -ensure $this.ensure -path $this.path -content $this.content
        return $get
    }

    <#
        This method is equivalent of the Set-TargetResource script function.
        It sets the resource to the desired state.
    #>
    [void] Set() {
        $set = Set-File -ensure $this.ensure -path $this.path -content $this.content
    }

    <#
        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() {
        $test = Test-File -ensure $this.ensure -path $this.path -content $this.content
        return $test
    }
}

マニフェストの作成

クラスベースのリソースを DSC エンジンで使用できるようにするには、マニフェスト ファイルに、リソースをエクスポートするようにモジュールに指示する DscResourcesToExport ステートメントを含める必要があります。 この例では、マニフェストは次のようになります。

@{

    # Script module or binary module file associated with this manifest.
    RootModule = 'NewFile.psm1'

    # Version number of this module.
    ModuleVersion = '1.0.0'

    # ID used to uniquely identify this module
    GUID = 'fad0d04e-65d9-4e87-aa17-39de1d008ee4'

    # Author of this module
    Author = 'Microsoft Corporation'

    # Company or vendor of this module
    CompanyName = 'Microsoft Corporation'

    # Copyright statement for this module
    Copyright = ''

    # Description of the functionality provided by this module
    Description = 'Create and set content of a file'

    # Minimum version of the Windows PowerShell engine required by this module
    PowerShellVersion = '5.0'

    # Functions to export from this module
    FunctionsToExport = @('Get-File','Set-File','Test-File')

    # DSC resources to export from this module
    DscResourcesToExport = @('NewFile')

    # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
    PrivateData = @{

        PSData = @{

            # Tags applied to this module. These help with module discovery in online galleries.
            # Tags = @(Power Plan, Energy, Battery)

            # A URL to the license for this module.
            # LicenseUri = ''

            # A URL to the main website for this project.
            # ProjectUri = ''

            # A URL to an icon representing this module.
            # IconUri = ''

            # ReleaseNotes of this module
            # ReleaseNotes = ''

        } # End of PSData hashtable

    }
}

リソースのテスト

既に説明したように、クラスとマニフェスト ファイルをフォルダー構造で保存した後で、新しいリソースを使用する構成を作成できます。 DSC 構成を実行する方法については、「構成の適用」をご覧ください。 次の構成では、ファイルが存在するかどうか、/tmp/test.txt にファイルが存在するかと、プロパティ 'Content' で指定される文字列とコンテンツが一致するかが確認されます。 一致しない場合、ファイル全体が書き込まれます。

Configuration MyConfig
{
    Import-DSCResource -ModuleName NewFile
    NewFile testFile
    {
        Path = "/tmp/test.txt"
        Content = "DSC Rocks!"
        Ensure = "Present"
    }
}
MyConfig

PsDscRunAsCredential のサポート

[注] PsDscRunAsCredential は、PowerShell 5.0 以降でサポートされています。

PsDscRunAsCredential プロパティを DSC 構成リソース ブロックで使用して、指定した資格情報のもとでリソースを実行する必要があることを指定できます。 詳細については、「ユーザーの資格情報を指定して DSC を実行する」を参照してください。

使用するリソースに対する PsDscRunAsCredential の要求または却下

DscResource() 属性でオプションのパラメーター RunAsCredential を指定します。 このパラメーターには以下の 3 つの値のいずれかを指定します。

  • OptionalPsDscRunAsCredential は、このリソースを呼び出す構成では省略可能です。 これが既定値です。
  • MandatoryPsDscRunAsCredential は、このリソースを呼び出す構成に使用する必要があります。
  • NotSupportedこのリソースを呼び出す構成には PsDscRunAsCredential を使用できません。
  • DefaultOptional と同じです。

たとえば、次の属性を使用して、ご使用のカスタム リソースが PsDscRunAsCredential の使用をサポートしないことを指定します。

[DscResource(RunAsCredential=NotSupported)]
class NewFile {
}

1 つのモジュールで複数のクラス リソースを宣言する

モジュールでは、複数のクラスベースの DSC リソースを定義できます。 同じ .psm1 ファイル内のすべてのクラスを宣言し、マニフェストに各名前を .psd1 含めるだけで済みます。

$env:ProgramFiles\WindowsPowerShell\Modules (folder)
     |- MyDscResource (folder)
        |- MyDscResource.psm1
           MyDscResource.psd1

ユーザー コンテキストへのアクセス

カスタム リソース内からユーザー コンテキストにアクセスするには、自動変数 $global:PsDscContext を使用できます。

たとえば、次のコードは、リソースが詳細出力ストリームに実行しているユーザー コンテキストを記述します。

if (PsDscContext.RunAsUser) {
    Write-Verbose "User: $global:PsDscContext.RunAsUser";
}

参照

Build Custom Windows PowerShell Desired State Configuration Resources (カスタム Windows PowerShell Desired State Configuration のビルド)