Scrittura di una risorsa DSC personalizzata con classi di PowerShell
Si applica a: Windows PowerShell 5.0
Con l'introduzione delle classi di PowerShell in Windows PowerShell 5.0, è ora possibile definire una risorsa DSC creando una classe . La classe definisce sia lo schema che l'implementazione della risorsa, quindi non è necessario creare un file MOF separato. Anche la struttura di cartelle per una risorsa basata su classi è più semplice, perché non è necessaria una cartella DSCResources
In una risorsa DSC basata su classi, lo schema viene definito come proprietà della classe che può essere modificata con attributi per specificare il tipo di proprietà. La risorsa viene implementata dai metodi Get()
, Set()
e Test()
(equivalenti alle funzioni Get-TargetResource
, Set-TargetResource
e Test-TargetResource
in una risorsa script.
In questo articolo verrà creata una risorsa semplice denominata NewFile che gestisce un file in un percorso specificato.
Per altre informazioni sulle risorse DSC, vedere Creare risorse di Configurazione dello stato desiderate di Windows PowerShell personalizzate
Nota
Le raccolte generiche non sono supportate nelle risorse basate su classi.
Struttura di cartelle per una risorsa di classe
Per implementare una risorsa personalizzata DSC con una classe PowerShell, creare la struttura di cartelle seguente.
La classe è definita in MyDscResource.psm1
e il manifesto del modulo è definito in MyDscResource.psd1
.
$env:ProgramFiles\WindowsPowerShell\Modules (folder)
|- MyDscResource (folder)
MyDscResource.psm1
MyDscResource.psd1
Creare la classe
Usare la parola chiave class per creare una classe di PowerShell. Per specificare che una classe è una risorsa DSC, usare l'attributo DscResource()
. Il nome della classe è il nome della risorsa DSC.
[DscResource()]
class NewFile {
}
Dichiarare le proprietà
Lo schema delle risorse DSC viene definito come proprietà della classe . Dichiariamo tre proprietà come indicato di seguito.
[DscProperty(Key)]
[string] $path
[DscProperty(Mandatory)]
[ensure] $ensure
[DscProperty()]
[string] $content
[DscProperty(NotConfigurable)]
[MyDscResourceReason[]] $Reasons
Si noti che le proprietà vengono modificate dagli attributi. Il significato degli attributi è il seguente:
- DscProperty(Key): la proprietà è obbligatoria. La proprietà è una chiave. I valori di tutte le proprietà contrassegnate come chiavi devono essere combinati per identificare in modo univoco un'istanza di risorsa all'interno di una configurazione.
- dscProperty(obbligatorio): la proprietà è obbligatoria.
-
DscProperty(NotConfigurable): la proprietà è di sola lettura. Le proprietà contrassegnate con questo attributo non possono essere impostate da una configurazione, ma vengono popolate dal metodo
Get()
quando presente. - DscProperty(): la proprietà è configurabile, ma non è obbligatoria.
Le proprietà $Path
e $SourcePath
sono entrambe stringhe. Il $CreationTime
è una proprietà dateTime. La proprietà $Ensure
è un tipo di enumerazione, definito come segue.
enum Ensure
{
Absent
Present
}
Classi di incorporamento
Se si vuole includere un nuovo tipo con proprietà definite che è possibile usare all'interno della risorsa, è sufficiente creare una classe con tipi di proprietà come descritto in precedenza.
class MyDscResourceReason {
[DscProperty()]
[string] $Code
[DscProperty()]
[string] $Phrase
}
Nota
La classe MyDscResourceReason
viene dichiarata qui con il nome del modulo come prefisso. Anche se è possibile assegnare un nome qualsiasi alle classi incorporate, se due o più moduli definiscono una classe con lo stesso nome e vengono entrambi usati in una configurazione, PowerShell genera un'eccezione.
Per evitare eccezioni causate da conflitti di nomi in DSC, anteporre i nomi delle classi incorporate con il nome del modulo. Se il nome della classe incorporata non è già in conflitto, è possibile usarlo senza un prefisso.
Se la risorsa DSC è progettata per l'uso con la funzionalità di configurazione del computer di Gestione automatica di Azure, anteporre sempre il nome della classe incorporata creata per la proprietà Reasons
Funzioni pubbliche e private
È possibile creare funzioni di PowerShell all'interno dello stesso file di modulo e usarle all'interno dei metodi della risorsa di classe DSC. Le funzioni devono essere dichiarate come pubbliche, ma i blocchi di script all'interno di tali funzioni pubbliche possono chiamare funzioni private. L'unica differenza è se sono elencati nella proprietà FunctionsToExport
del manifesto del modulo.
<#
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
}
Implementazione dei metodi
I metodi Get()
, Set()
e Test()
sono analoghi alle funzioni Get-TargetResource
, Set-TargetResource
e Test-TargetResource
in una risorsa script.
Come procedura consigliata, ridurre al minimo la quantità di codice all'interno dell'implementazione della classe. Spostare invece la maggior parte del codice in funzioni pubbliche nel modulo, che può quindi essere testato in modo indipendente.
<#
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
}
File completo
Di seguito è indicato il file di classe completo.
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
}
}
Creare un manifesto
Per rendere disponibile una risorsa basata su classi per il motore DSC, è necessario includere un'istruzione DscResourcesToExport
nel file manifesto che indica al modulo di esportare la risorsa. Il manifesto è simile al seguente:
@{
# 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
}
}
Testare la risorsa
Dopo aver salvato i file di classe e manifesto nella struttura di cartelle come descritto in precedenza, è possibile creare una configurazione che usa la nuova risorsa. Per informazioni su come eseguire una configurazione DSC, vedere Applicazione delle configurazioni. La configurazione seguente verificherà se il file in /tmp/test.txt
esiste e se il contenuto corrisponde alla stringa fornita dalla proprietà 'Content'. In caso contrario, viene scritto l'intero file.
Configuration MyConfig
{
Import-DSCResource -ModuleName NewFile
NewFile testFile
{
Path = "/tmp/test.txt"
Content = "DSC Rocks!"
Ensure = "Present"
}
}
MyConfig
Supporto di PsDscRunAsCredential
[Nota] PsDscRunAsCredential è supportato in PowerShell 5.0 e versioni successive.
La proprietà PsDscRunAsCredential può essere usata nelle configurazioni DSC blocco di risorse per specificare che la risorsa deve essere eseguita in un set specificato di credenziali. Per altre informazioni, vedere Esecuzione di DSC con credenziali utente.
Richiedere o non consentire PsDscRunAsCredential per la risorsa
L'attributo DscResource()
accetta un parametro facoltativo RunAsCredential. Questo parametro accetta uno dei tre valori seguenti:
-
Optional
PsDscRunAsCredential è facoltativo per le configurazioni che chiamano questa risorsa. Questo è il valore predefinito. -
Mandatory
PsDscRunAsCredential deve essere usato per qualsiasi configurazione che chiama questa risorsa. -
NotSupported
Configurazioni che chiamano questa risorsa non possono usare PsDscRunAsCredential. -
Default
Uguale aOptional
.
Ad esempio, usare l'attributo seguente per specificare che la risorsa personalizzata non supporta l'uso di PsDscRunAsCredential:
[DscResource(RunAsCredential=NotSupported)]
class NewFile {
}
Dichiarazione di più risorse di classe in un modulo
Un modulo può definire più risorse DSC basate su classi. È sufficiente dichiarare tutte le classi nello stesso file .psm1
e includere ogni nome nel manifesto .psd1
.
$env:ProgramFiles\WindowsPowerShell\Modules (folder)
|- MyDscResource (folder)
|- MyDscResource.psm1
MyDscResource.psd1
Accedere al contesto utente
Per accedere al contesto utente dall'interno di una risorsa personalizzata, è possibile usare la variabile automatica $global:PsDscContext
.
Ad esempio, il codice seguente scriverà il contesto utente in cui la risorsa è in esecuzione nel flusso di output dettagliato:
if (PsDscContext.RunAsUser) {
Write-Verbose "User: $global:PsDscContext.RunAsUser";
}
Vedere anche
Compilare risorse personalizzate di Windows PowerShell DSC (Desired State Configuration)