Delen via


Een aangepaste DSC-resource schrijven met PowerShell-klassen

Van toepassing op: Windows PowerShell 5.0

Met de introductie van PowerShell-klassen in Windows PowerShell 5.0 kunt u nu een DSC-resource definiëren door een klasse te maken. De klasse definieert zowel het schema als de implementatie van de resource, dus u hoeft geen afzonderlijk MOF-bestand te maken. De mapstructuur voor een op klassen gebaseerde resource is ook eenvoudiger, omdat een DSCResources- map niet nodig is.

In een DSC-resource op basis van een klasse wordt het schema gedefinieerd als eigenschappen van de klasse die kan worden gewijzigd met kenmerken om het eigenschapstype op te geven. De resource wordt geïmplementeerd door Get(), Set()en Test() methoden (gelijk aan de Get-TargetResource, Set-TargetResourceen Test-TargetResource functies in een scriptresource.

In dit artikel maken we een eenvoudige resource met de naam NewFile waarmee een bestand in een opgegeven pad wordt beheerd.

Zie Aangepaste Windows PowerShell Desired State Configuration-resources bouwen voor meer informatie over DSC-resources

Notitie

Algemene verzamelingen worden niet ondersteund in op klassen gebaseerde resources.

Mapstructuur voor een klasseresource

Als u een aangepaste DSC-resource wilt implementeren met een PowerShell-klasse, maakt u de volgende mapstructuur. De klasse wordt gedefinieerd in MyDscResource.psm1 en het modulemanifest wordt gedefinieerd in MyDscResource.psd1.

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

De klasse maken

U gebruikt het trefwoord klasse om een PowerShell-klasse te maken. Als u wilt opgeven dat een klasse een DSC-resource is, gebruikt u het kenmerk DscResource(). De naam van de klasse is de naam van de DSC-resource.

[DscResource()]
class NewFile {
}

Eigenschappen declareren

Het DSC-resourceschema wordt gedefinieerd als eigenschappen van de klasse. We declareren drie eigenschappen als volgt.

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

U ziet dat de eigenschappen worden gewijzigd door kenmerken. De betekenis van de kenmerken is als volgt:

  • DscProperty(Key): de eigenschap is vereist. De eigenschap is een sleutel. De waarden van alle eigenschappen die als sleutels zijn gemarkeerd, moeten worden gecombineerd om een resource-exemplaar in een configuratie uniek te identificeren.
  • DscProperty(Verplicht): de eigenschap is vereist.
  • DscProperty(NotConfigurable): de eigenschap heeft het kenmerk Alleen-lezen. Eigenschappen die zijn gemarkeerd met dit kenmerk kunnen niet worden ingesteld door een configuratie, maar worden ingevuld door de Get() methode wanneer deze aanwezig zijn.
  • DscProperty(): de eigenschap kan worden geconfigureerd, maar is niet vereist.

De eigenschappen $Path en $SourcePath zijn beide tekenreeksen. De $CreationTime is een eigenschap DateTime. De eigenschap $Ensure is een opsommingstype dat als volgt is gedefinieerd.

enum Ensure
{
    Absent
    Present
}

Klassen insluiten

Als u een nieuw type wilt opnemen met gedefinieerde eigenschappen die u in uw resource kunt gebruiken, maakt u een klasse met eigenschapstypen zoals hierboven beschreven.

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

    [DscProperty()]
    [string] $Phrase
}

Notitie

De MyDscResourceReason klasse wordt hier gedeclareerd met de naam van de module als voorvoegsel. Hoewel u ingesloten klassen een willekeurige naam kunt geven, als twee of meer modules een klasse met dezelfde naam definiëren en beide worden gebruikt in een configuratie, genereert PowerShell een uitzondering.

Als u uitzonderingen wilt voorkomen die worden veroorzaakt door naamconflicten in DSC, moet u de namen van uw ingesloten klassen vooraf laten gaan door de modulenaam. Als de naam van uw ingesloten klasse al niet conflicteert, kunt u deze gebruiken zonder voorvoegsel.

Als uw DSC-resource is ontworpen voor gebruik met de machineconfiguratiefunctie van Azure Automanage, moet u altijd de naam van de ingesloten klasse vooraf laten gaan die u maakt voor de eigenschap Redenen.

Openbare en privéfuncties

U kunt PowerShell-functies maken in hetzelfde modulebestand en deze gebruiken in de methoden van uw DSC-klasseresource. De functies moeten als openbaar worden gedeclareerd, maar de scriptblokken binnen deze openbare functies kunnen functies aanroepen die privé zijn. Het enige verschil is of ze worden vermeld in de eigenschap FunctionsToExport van het modulemanifest.

<#
   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
}

De methoden implementeren

De methoden Get(), Set()en Test() zijn vergelijkbaar met de functies Get-TargetResource, Set-TargetResourceen Test-TargetResource in een scriptresource.

Minimaliseer als best practice de hoeveelheid code binnen de klasse-implementatie. Verplaats in plaats daarvan het merendeel van uw code naar openbare functies in de module, die vervolgens onafhankelijk kunnen worden getest.

<#
    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
}

Het volledige bestand

Het volledige klassebestand volgt.

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
    }
}

Een manifest maken

Als u een klasseresource beschikbaar wilt maken voor de DSC-engine, moet u een DscResourcesToExport-instructie opnemen in het manifestbestand waarmee de module wordt geïnstrueerd om de resource te exporteren. Ons manifest ziet er als volgt uit:

@{

    # 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

    }
}

De resource testen

Nadat u de klasse- en manifestbestanden in de mapstructuur hebt opgeslagen zoals eerder is beschreven, kunt u een configuratie maken die gebruikmaakt van de nieuwe resource. Zie Configuraties uitvoerenvoor meer informatie over het uitvoeren van een DSC-configuratie. Met de volgende configuratie wordt gecontroleerd of het bestand op /tmp/test.txt bestaat en of de inhoud overeenkomt met de tekenreeks van de eigenschap 'Inhoud'. Zo niet, dan wordt het hele bestand geschreven.

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

Ondersteuning voor PsDscRunAsCredential

[Opmerking] PsDscRunAsCredential- wordt ondersteund in PowerShell 5.0 en hoger.

De eigenschap PsDscRunAsCredential kan worden gebruikt in DSC-configuraties resourceblok om op te geven dat de resource moet worden uitgevoerd onder een opgegeven set referenties. Zie DSC uitvoeren met gebruikersreferentiesvoor meer informatie.

PsDscRunAsCredential vereisen of weigeren voor uw resource

Het kenmerk DscResource() gebruikt een optionele parameter RunAsCredential-. Deze parameter heeft een van de drie waarden:

  • Optional PsDscRunAsCredential- is optioneel voor configuraties die deze resource aanroepen. Dit is de standaardwaarde.
  • Mandatory PsDscRunAsCredential- moet worden gebruikt voor elke configuratie die deze resource aanroept.
  • NotSupported Configuraties die deze resource aanroepen, kunnen PsDscRunAsCredential-niet gebruiken.
  • Default Hetzelfde als Optional.

Gebruik bijvoorbeeld het volgende kenmerk om op te geven dat uw aangepaste resource geen ondersteuning biedt voor het gebruik van PsDscRunAsCredential-:

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

Meerdere klassebronnen declareren in een module

Een module kan meerdere DSC-resources op basis van klassen definiëren. U hoeft alleen alle klassen in hetzelfde .psm1 bestand te declareren en elke naam op te nemen in het .psd1 manifest.

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

Toegang tot de gebruikerscontext

Als u toegang wilt krijgen tot de gebruikerscontext vanuit een aangepaste resource, kunt u de automatische variabele $global:PsDscContextgebruiken.

Met de volgende code wordt bijvoorbeeld de gebruikerscontext geschreven waaronder de resource wordt uitgevoerd naar de uitgebreide uitvoerstroom:

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

Zie ook

Aangepaste Windows PowerShell Desired State Configuration-resources maken