撰寫類別型 DSC 資源
您可以藉由建立 PowerShell 類別來定義 DSC 資源。 在類別型 DSC 資源中,架構會定義為類別的屬性,這些屬性可以使用屬性來修改,以指定屬性類型。 資源是使用 Get、 Set 和 Test 方法實作, (等於 Get-TargetResource
資源) 腳本中的 、 Set-TargetResource
和 Test-TargetResource
函式。
在本文中,我們會建立名為 NewFile
的最小資源,以管理指定路徑中的檔案。
如需 DSC 資源的詳細資訊,請參閱 DSC 資源。
注意
類別型資源不支援泛型集合。
類別資源的資料夾結構
若要使用PowerShell類別實作 DSC 資源,請建立下列資料夾結構。 此類別在 MyDscResource.psm1
中定義,而模組資訊清單則在 MyDscResource.psd1
中定義。
$env:ProgramFiles\WindowsPowerShell\Modules (folder)
|- MyDscResource (folder)
MyDscResource.psm1
MyDscResource.psd1
建立類別
您可以使用 class
關鍵詞來建立 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) :此為唯讀屬性。 以這個屬性標示的屬性無法由組態設定,但會由 Get 方法填入。
- DscProperty () :屬性是可設定的,但並非必要。
Path 和 SourcePath 屬性都是字串。 CreationTime 是 DateTime 屬性。 Ensure 屬性是列舉型別,定義如下。
enum Ensure {
Absent
Present
}
內嵌類別
如果您想要包含可在 DSC 資源內使用之已定義屬性的新類型,請依照先前所述,建立具有屬性類型的類別。
class MyDscResourceReason {
[DscProperty()]
[string] $Code
[DscProperty()]
[string] $Phrase
}
注意
類別 MyDscResourceReason
在此宣告為模組的名稱作為前置詞。 雖然您可以為內嵌類別提供任何名稱,但如果兩個以上的模組定義具有相同名稱且兩者都用於設定中的類別,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 $path -ErrorAction SilentlyContinue
if ($fileExists) {
$filePresent.phrase = @(
"The file was expected to be: $ensure"
"The file exists at path: $path"
) -join "`n"
$existingFileContent = Get-Content $path -Raw
if ([string]::IsNullOrEmpty($existingFileContent)) {
$existingFileContent = ''
}
if (![string]::IsNullOrEmpty($content)) {
$content = $content | ConvertTo-SpecialChars
}
$fileContent.phrase = @(
"The file was expected to contain: $content"
"The file contained: $existingFileContent"
) -join "`n"
if ($content -eq $existingFileContent) {
$ensureReturn = 'Present'
}
} else {
$filePresent.phrase = @(
"The file was expected to be: $ensure"
"The file does not exist at path: $path"
) -join "`n"
$path = 'file not found'
}
@{
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
}
$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])
}
$string
}
實作方法
Get、Set 和 Test 方法類似於Get-TargetResource
腳本資源中的、 Set-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 $path -ErrorAction SilentlyContinue
if ($fileExists) {
$filePresent.phrase = @(
"The file was expected to be: $ensure"
"The file exists at path: $path"
) -join "`n"
$existingFileContent = Get-Content $path -Raw
if ([string]::IsNullOrEmpty($existingFileContent)) {
$existingFileContent = ''
}
if (![string]::IsNullOrEmpty($content)) {
$content = $content | ConvertTo-SpecialChars
}
$fileContent.phrase = @(
"The file was expected to contain: $content"
"The file contained: $existingFileContent"
) -join "`n"
if ($content -eq $existingFileContent) {
$ensureReturn = 'Present'
}
} else {
$filePresent.phrase = @(
"The file was expected to be: $ensure"
"The file does not exist at path: $path"
) -join "`n"
$path = 'file not found'
}
@{
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
在指令清單檔中包含 語句,以指示模組匯出 DSC 資源。 我們的資訊清單看起來像這樣︰
@{
# 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 資源的 DSC 組態。 下列Configuration
會檢查檔案是否存在,以及內容是否符合/tmp/test.txt
屬性 Content 所提供的字串。 如果沒有,則會寫入整個檔案。
Configuration MyConfig {
Import-DSCResource -ModuleName NewFile
NewFile testFile {
Path = "/tmp/test.txt"
Content = "DSC Rocks!"
Ensure = "Present"
}
}
MyConfig
在模組中宣告多個類別型 DSC 資源
模組可以定義多個類別型 DSC 資源。 您必須宣告相同 .psm1
檔案中的所有類別,並在指令清單中包含 .psd1
每個名稱。
$env:ProgramFiles\PowerShell\Modules (folder)
|- MyDscResource (folder)
|- MyDscResource.psm1
MyDscResource.psd1
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應