Schreiben einer benutzerdefinierten DSC-Ressource mit PowerShell-Klassen
Gilt für: Windows PowerShell 5.0
Mit der Einführung von PowerShell-Klassen in Windows PowerShell 5.0 können Sie jetzt eine DSC-Ressource definieren, indem Sie eine Klasse erstellen. Die Klasse definiert sowohl das Schema als auch die Implementierung der Ressource, sodass keine separate MOF-Datei erstellt werden muss. Die Ordnerstruktur für eine klassenbasierte Ressource ist ebenfalls einfacher, da ein DSCResources Ordner nicht erforderlich ist.
In einer klassenbasierten DSC-Ressource wird das Schema als Eigenschaften der Klasse definiert, die mit Attributen geändert werden können, um den Eigenschaftentyp anzugeben. Die Ressource wird durch die Methoden Get()
, Set()
und Test()
implementiert (entspricht den Funktionen Get-TargetResource
, Set-TargetResource
und Test-TargetResource
in einer Skriptressource.
In diesem Artikel erstellen wir eine einfache Ressource namens NewFile-, die eine Datei in einem angegebenen Pfad verwaltet.
Weitere Informationen zu DSC-Ressourcen finden Sie unter Erstellen von benutzerdefinierten Windows PowerShell Desired State Configuration Resources
Anmerkung
Generische Auflistungen werden in klassenbasierten Ressourcen nicht unterstützt.
Ordnerstruktur für eine Klassenressource
Um eine benutzerdefinierte DSC-Ressource mit einer PowerShell-Klasse zu implementieren, erstellen Sie die folgende Ordnerstruktur.
Die Klasse wird in MyDscResource.psm1
definiert, und das Modulmanifest wird in MyDscResource.psd1
definiert.
$env:ProgramFiles\WindowsPowerShell\Modules (folder)
|- MyDscResource (folder)
MyDscResource.psm1
MyDscResource.psd1
Erstellen der Klasse
Sie verwenden das Klassenschlüsselwort, um eine PowerShell-Klasse zu erstellen. Um anzugeben, dass eine Klasse eine DSC-Ressource ist, verwenden Sie das attribut DscResource()
. Der Name der Klasse ist der Name der DSC-Ressource.
[DscResource()]
class NewFile {
}
Deklarieren von Eigenschaften
Das DSC-Ressourcenschema wird als Eigenschaften der Klasse definiert. Wir deklarieren drei Eigenschaften wie folgt.
[DscProperty(Key)]
[string] $path
[DscProperty(Mandatory)]
[ensure] $ensure
[DscProperty()]
[string] $content
[DscProperty(NotConfigurable)]
[MyDscResourceReason[]] $Reasons
Beachten Sie, dass die Eigenschaften durch Attribute geändert werden. Die Bedeutung der Attribute lautet wie folgt:
- DscProperty(Key): Die Eigenschaft ist erforderlich. Die Eigenschaft ist ein Schlüssel. Die Werte aller Eigenschaften, die als Schlüssel gekennzeichnet sind, müssen kombiniert werden, um eine Ressourceninstanz innerhalb einer Konfiguration eindeutig zu identifizieren.
- DscProperty(Required): Die Eigenschaft ist erforderlich.
-
DscProperty(NotConfigurable): Die Eigenschaft ist schreibgeschützt. Eigenschaften, die mit diesem Attribut gekennzeichnet sind, können nicht durch eine Konfiguration festgelegt werden, werden aber von der
Get()
Methode aufgefüllt, wenn vorhanden. - DscProperty(): Die Eigenschaft kann konfiguriert werden, ist jedoch nicht erforderlich.
Die Eigenschaften $Path
und $SourcePath
sind beide Zeichenfolgen. Die $CreationTime
ist eine DateTime--Eigenschaft. Die $Ensure
-Eigenschaft ist ein Enumerationstyp, der wie folgt definiert ist.
enum Ensure
{
Absent
Present
}
Einbetten von Klassen
Wenn Sie einen neuen Typ mit definierten Eigenschaften einschließen möchten, die Sie in Ihrer Ressource verwenden können, erstellen Sie einfach eine Klasse mit Eigenschaftstypen, wie oben beschrieben.
class MyDscResourceReason {
[DscProperty()]
[string] $Code
[DscProperty()]
[string] $Phrase
}
Anmerkung
Die MyDscResourceReason
Klasse wird hier mit dem Namen des Moduls als Präfix deklariert. Wenn zwei oder mehr Module eine Klasse mit demselben Namen definieren und beide in einer Konfiguration verwendet werden, löst PowerShell eine Ausnahme aus.
Um Ausnahmen zu vermeiden, die durch Namenskonflikte in DSC verursacht werden, stellen Sie den Namen ihrer eingebetteten Klassen den Modulnamen voran. Wenn der Name Ihrer eingebetteten Klasse bereits nicht in Konflikt steht, können Sie sie ohne Präfix verwenden.
Wenn Ihre DSC-Ressource für die Verwendung mit dem Computerkonfigurationsfeature von Azure Automanage entwickelt wurde, stellen Sie immer dem Namen der eingebetteten Klasse voran, die Sie für die Reasons-Eigenschaft erstellen.
Öffentliche und private Funktionen
Sie können PowerShell-Funktionen innerhalb derselben Moduldatei erstellen und in den Methoden Ihrer DSC-Klassenressource verwenden. Die Funktionen müssen als öffentlich deklariert werden, aber die Skriptblöcke innerhalb dieser öffentlichen Funktionen können Funktionen aufrufen, die privat sind. Der einzige Unterschied besteht darin, ob sie in der FunctionsToExport
Eigenschaft des Modulmanifests aufgeführt werden.
<#
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
}
Implementieren der Methoden
Die Methoden Get()
, Set()
und Test()
sind analog zu den Funktionen Get-TargetResource
, Set-TargetResource
und Test-TargetResource
in einer Skriptressource.
Minimieren Sie als bewährte Methode die Menge des Codes innerhalb der Klassenimplementierung. Verschieben Sie stattdessen den Großteil Ihres Codes in öffentliche Funktionen im Modul, die dann unabhängig getestet werden können.
<#
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
}
Die vollständige Datei
Die vollständige Klassendatei folgt.
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
}
}
Erstellen eines Manifests
Um eine klassenbasierte Ressource für das DSC-Modul verfügbar zu machen, müssen Sie eine DscResourcesToExport
-Anweisung in die Manifestdatei einschließen, die das Modul anweist, die Ressource zu exportieren. Unser Manifest sieht wie folgt aus:
@{
# 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
}
}
Testen der Ressource
Nachdem Sie die Klassen- und Manifestdateien wie zuvor beschrieben in der Ordnerstruktur gespeichert haben, können Sie eine Konfiguration erstellen, die die neue Ressource verwendet. Informationen zum Ausführen einer DSC-Konfiguration finden Sie unter Ersetzung von Konfigurationen. Die folgende Konfiguration überprüft, ob die Datei bei /tmp/test.txt
vorhanden ist und ob der Inhalt mit der von der Eigenschaft "Content" bereitgestellten Zeichenfolge übereinstimmt. Wenn nicht, wird die gesamte Datei geschrieben.
Configuration MyConfig
{
Import-DSCResource -ModuleName NewFile
NewFile testFile
{
Path = "/tmp/test.txt"
Content = "DSC Rocks!"
Ensure = "Present"
}
}
MyConfig
Unterstützen von PsDscRunAsCredential
[Hinweis] PsDscRunAsCredential- wird in PowerShell 5.0 und höher unterstützt.
Die PsDscRunAsCredential--Eigenschaft kann in DSC-Konfigurationen Ressourcenblocks verwendet werden, um anzugeben, dass die Ressource unter einer angegebenen Gruppe von Anmeldeinformationen ausgeführt werden soll. Weitere Informationen finden Sie unter Ausführen von DSC mit Benutzeranmeldeinformationen.
Anfordern oder Verbieten von PsDscRunAsCredential für Ihre Ressource
Das attribut DscResource()
akzeptiert einen optionalen Parameter RunAsCredential. Dieser Parameter akzeptiert einen von drei Werten:
-
Optional
PsDscRunAsCredential ist optional für Konfigurationen, die diese Ressource aufrufen. Dies ist der Standardwert. -
Mandatory
PsDscRunAsCredential- muss für jede Konfiguration verwendet werden, die diese Ressource aufruft. -
NotSupported
Konfigurationen, die diese Ressource aufrufen, können PsDscRunAsCredentialnicht verwenden. -
Default
Identisch mitOptional
.
Verwenden Sie beispielsweise das folgende Attribut, um anzugeben, dass Ihre benutzerdefinierte Ressource die Verwendung von PsDscRunAsCredential-nicht unterstützt:
[DscResource(RunAsCredential=NotSupported)]
class NewFile {
}
Deklarieren mehrerer Klassenressourcen in einem Modul
Ein Modul kann mehrere klassenbasierte DSC-Ressourcen definieren. Sie müssen nur alle Klassen in derselben .psm1
Datei deklarieren und jeden Namen in das .psd1
-Manifest einschließen.
$env:ProgramFiles\WindowsPowerShell\Modules (folder)
|- MyDscResource (folder)
|- MyDscResource.psm1
MyDscResource.psd1
Zugreifen auf den Benutzerkontext
Um in einer benutzerdefinierten Ressource auf den Benutzerkontext zuzugreifen, können Sie die automatische Variable $global:PsDscContext
verwenden.
Der folgende Code würde z. B. den Benutzerkontext schreiben, unter dem die Ressource im ausführlichen Ausgabedatenstrom ausgeführt wird:
if (PsDscContext.RunAsUser) {
Write-Verbose "User: $global:PsDscContext.RunAsUser";
}