共用方式為


使用 PowerShell 類別撰寫自訂的 DSC 資源

適用於:Windows PowerShell 5.0

您可以利用 Windows PowerShell 5.0 引入的 PowerShell 類別,藉由建立類別來定義 DSC 資源。 類別會定義結構描述和資源實作,所以不必建立個別的 MOF 檔案。 以類別為基礎的資源資料夾結構也比較簡單,因為不需要 DSCResources 資料夾。

在以類別為基礎的 DSC 資源中,架構會定義為類別的屬性,而該屬性可以使用屬性來修改,以指定屬性類型。 資源是由 Get()Set()Test() 方法實作,它們相當於指令碼資源的 Get-TargetResourceSet-TargetResourceTest-TargetResource 函式。

在本文中,我們將建立名為 NewFile 的簡單資源,以管理指定路徑中的檔案。

如需 DSC 資源的詳細資訊,請參閱建置自訂的 Windows PowerShell 預期狀態設定資源

注意

以類別為基礎的資源不支援泛型集合。

類別資源的資料夾結構

若要使用 PowerShell 類別實作 DSC 自訂資源,請建立下列資料夾結構。 此類別在 MyDscResource.psm1 中定義,而模組資訊清單則在 MyDscResource.psd1 中定義。

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

建立類別

您要使用類別關鍵字來建立 PowerShell 類別。 若要明確指出類別是 DSC 資源,請使用 DscResource() 屬性。 類別名稱是 DSC 資源的名稱。

[DscResource()]
class NewFile {
}

宣告屬性

DSC 資源結構描述會定義為類別的屬性。 我們會宣告三個屬性,如下所示。

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

請注意,屬性 (attribute) 會修改屬性 (property)。 屬性的意義如下:

  • DscProperty(Key) :此為必要屬性。 此屬性為索引鍵。 所有標示為索引鍵的屬性值都必須結合,以在設定內唯一識別資源執行個體。
  • DscProperty(Mandatory) :此為必要屬性。
  • DscProperty(NotConfigurable) :此為唯讀屬性。 標示了這個屬性 (Attribute) 的屬性 (Property) 無法由設定進行設定,但出現時會由 Get() 方法填入。
  • DscProperty() :此為可設定但非必要的屬性。

$Path$SourcePath 屬性都是字串。 $CreationTimeDateTime 屬性。 $Ensure 屬性是列舉類型,定義如下。

enum Ensure
{
    Absent
    Present
}

內嵌類別

如果您想要包含可在資源內使用之已定義屬性的新類型,只要建立具有屬性類型的類別,如上所述。

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

    [DscProperty()]
    [string] $Phrase
}

注意

類別 MyDscResourceReason 在此宣告為模組的名稱做為前置詞。 雖然您可以為內嵌類別提供任何名稱,但如果兩個以上的模組定義具有相同名稱的類別,而且兩者都用於組態中,PowerShell 就會引發例外狀況。

若要避免 DSC 中名稱衝突所造成的例外狀況,請在內嵌類別的名稱前面加上模組名稱。 如果內嵌類別的名稱已經不太可能發生衝突,您可以使用它而不使用前置詞。

如果您的 DSC 資源是設計來搭配 Azure Automanage 的機器組態功能使用,請一律在您為 Reasons 屬性建立的內嵌類別名稱前面加上前置詞。

公用和私人函式

您可以在相同的模組檔案內建立PowerShell函式,並在 DSC 類別資源的方法內使用這些函式。 函式必須宣告為 public,不過這些公用函式內的腳本區塊可以呼叫私用的函式。 唯一的差異在於它們是否列在 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-TargetResourceTest-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

[注意] PowerShell 5.0 及更新版本支援 PsDscRunAsCredential

您可以在 DSC 設定資源區塊中使用 PsDscRunAsCredential 特性,以指定該資源應該在一組指定的認證下執行。 如需詳細資訊,請參閱以使用者認證執行 DSC

針對您的資源要求使用或不允許使用 PsDscRunAsCredential

DscResource() 屬性可接受選擇性的參數 RunAsCredential。 此參數可接受下列三個值其中之一:

  • OptionalPsDscRunAsCredential 對於呼叫此資源的組態而言是選擇性的。 這是預設值。
  • MandatoryPsDscRunAsCredential 必須用於呼叫此資源的任何設定。
  • NotSupported:呼叫此資源的設定無法使用 PsDscRunAsCredential
  • Default:與 Optional 相同。

例如,使用下列屬性可以指定您的自訂資源不支援使用 PsDscRunAsCredential

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

在模組中宣告多個類別資源

模組可以定義多個以類別為基礎的 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";
}

另請參閱

建置自訂的 Windows PowerShell 預期狀態設定資源