Skriva en anpassad DSC-resurs med PowerShell-klasser

Gäller för: Windows PowerShell 5.0

Med introduktionen av PowerShell-klasser i Windows PowerShell 5.0 kan du nu definiera en DSC-resurs genom att skapa en klass. Klassen definierar både schemat och implementeringen av resursen, så det finns inget behov av att skapa en separat MOF-fil. Mappstrukturen för en klassbaserad resurs är också enklare eftersom det inte behövs någon DSCResources-mapp .

I en klassbaserad DSC-resurs definieras schemat som egenskaper för klassen som kan ändras med attribut för att ange egenskapstypen. Resursen implementeras av Get()metoderna , Set()och Test() (motsvarar Get-TargetResourcefunktionerna , Set-TargetResourceoch Test-TargetResource i en skriptresurs.

I den här artikeln skapar vi en enkel resurs med namnet NewFile som hanterar en fil i en angiven sökväg.

Mer information om DSC-resurser finns i Skapa anpassade Windows PowerShell Desired State Configuration-resurser

Anteckning

Allmänna samlingar stöds inte i klassbaserade resurser.

Mappstruktur för en klassresurs

Om du vill implementera en anpassad DSC-resurs med en PowerShell-klass skapar du följande mappstruktur. Klassen definieras i MyDscResource.psm1 och modulmanifestet definieras i MyDscResource.psd1.

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

Skapa klassen

Du använder klassnyckelordet för att skapa en PowerShell-klass. Om du vill ange att en klass är en DSC-resurs använder du attributet DscResource() . Namnet på klassen är namnet på DSC-resursen.

[DscResource()]
class NewFile {
}

Deklarera egenskaper

DSC-resursschemat definieras som egenskaper för klassen. Vi deklarerar tre egenskaper enligt följande.

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

Observera att egenskaperna ändras av attribut. Attributens betydelse är följande:

  • DscProperty(Key): Egenskapen krävs. Egenskapen är en nyckel. Värdena för alla egenskaper som har markerats som nycklar måste kombineras för att unikt identifiera en resursinstans i en konfiguration.
  • DscProperty(Obligatoriskt): Egenskapen krävs.
  • DscProperty(NotConfigurable): Egenskapen är skrivskyddad. Egenskaper som har markerats med det här attributet kan inte anges av en konfiguration, men fylls i av metoden när de Get() finns.
  • DscProperty(): Egenskapen kan konfigureras, men den krävs inte.

Egenskaperna $Path och $SourcePath är båda strängarna. $CreationTime är en DateTime-egenskap. Egenskapen $Ensure är en uppräkningstyp som definieras på följande sätt.

enum Ensure
{
    Absent
    Present
}

Inbäddningsklasser

Om du vill inkludera en ny typ med definierade egenskaper som du kan använda i resursen skapar du bara en klass med egenskapstyper enligt beskrivningen ovan.

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

    [DscProperty()]
    [string] $Phrase
}

Anteckning

Klassen MyDscResourceReason deklareras här med modulens namn som prefix. Du kan ge inbäddade klasser valfritt namn, men om två eller flera moduler definierar en klass med samma namn och båda används i en konfiguration, genererar PowerShell ett undantag.

Om du vill undvika undantag som orsakas av namnkonflikter i DSC prefixar du namnen på dina inbäddade klasser med modulnamnet. Om namnet på din inbäddade klass redan inte är i konflikt kan du använda det utan prefix.

Om din DSC-resurs är utformad för användning med Azure Automanages datorkonfigurationsfunktion, prefixet alltid namnet på den inbäddade klassen som du skapar för egenskapen Reasons .

Offentliga och privata funktioner

Du kan skapa PowerShell-funktioner i samma modulfil och använda dem i metoderna för din DSC-klassresurs. Funktionerna måste deklareras som offentliga, men skriptblocken i dessa offentliga funktioner kan anropa funktioner som är privata. Den enda skillnaden är om de visas i FunctionsToExport egenskapen för modulmanifestet.

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

Implementera metoderna

Metoderna Get(), Set()och Test() är analoga med Get-TargetResourcefunktionerna , Set-TargetResourceoch Test-TargetResource i en skriptresurs.

Vi rekommenderar att du minimerar mängden kod i klassimplementeringen. Flytta i stället merparten av koden till offentliga funktioner i modulen, som sedan kan testas oberoende av varandra.

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

Den fullständiga filen

Den fullständiga klassfilen följer.

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

Skapa ett manifest

Om du vill göra en klassbaserad resurs tillgänglig för DSC-motorn måste du inkludera en DscResourcesToExport instruktion i manifestfilen som instruerar modulen att exportera resursen. Vårt manifest ser ut så här:

@{

    # 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

    }
}

Testa resursen

När du har sparat klass- och manifestfilerna i mappstrukturen enligt beskrivningen tidigare kan du skapa en konfiguration som använder den nya resursen. Information om hur du kör en DSC-konfiguration finns i Anta konfigurationer. Följande konfiguration kontrollerar om filen /tmp/test.txt finns och om innehållet matchar strängen som tillhandahålls av egenskapen "Innehåll". Annars skrivs hela filen.

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

Stöd för PsDscRunAsCredential

[Obs] PsDscRunAsCredential stöds i PowerShell 5.0 och senare.

Egenskapen PsDscRunAsCredential kan användas i resursblocket för DSC-konfigurationer för att ange att resursen ska köras under en angiven uppsättning autentiseringsuppgifter. Mer information finns i Köra DSC med användarautentiseringsuppgifter.

Kräv eller tillåt inte PsDscRunAsCredential för din resurs

Attributet DscResource() tar en valfri parameter RunAsCredential. Den här parametern tar ett av tre värden:

  • OptionalPsDscRunAsCredential är valfritt för konfigurationer som anropar den här resursen. Detta är standardvärdet.
  • MandatoryPsDscRunAsCredential måste användas för alla konfigurationer som anropar den här resursen.
  • NotSupported Konfigurationer som anropar den här resursen kan inte använda PsDscRunAsCredential.
  • Default Samma som Optional.

Använd till exempel följande attribut för att ange att din anpassade resurs inte stöder användning av PsDscRunAsCredential:

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

Deklarera flera klassresurser i en modul

En modul kan definiera flera klassbaserade DSC-resurser. Du behöver bara deklarera alla klasser i samma .psm1 fil och inkludera varje namn i manifestet .psd1 .

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

Komma åt användarkontexten

Om du vill komma åt användarkontexten inifrån en anpassad resurs kan du använda den automatiska variabeln $global:PsDscContext.

Följande kod skulle till exempel skriva användarkontexten under vilken resursen körs till den utförliga utdataströmmen:

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

Se även

Skapa anpassade Windows PowerShell Desired State Configuration-resurser