Compartir a través de


Escritura de un recurso de DSC personalizado con clases de PowerShell

Se aplica a: Windows PowerShell 5.0

Con la introducción de clases de PowerShell en Windows PowerShell 5.0, ahora puede definir un recurso de DSC mediante la creación de una clase . La clase define el esquema y la implementación del recurso, por lo que no es necesario crear un archivo MOF independiente. La estructura de carpetas de un recurso basado en clases también es más sencilla, ya que no es necesaria una carpeta de DSCResources.

En un recurso de DSC basado en clases, el esquema se define como propiedades de la clase que se puede modificar con atributos para especificar el tipo de propiedad. El recurso se implementa mediante métodos Get(), Set()y Test() (equivalentes a las funciones de Get-TargetResource, Set-TargetResourcey Test-TargetResource en un recurso de script.

En este artículo, crearemos un recurso simple denominado NewFile que administra un archivo en una ruta de acceso especificada.

Para obtener más información sobre los recursos de DSC, consulte Build Custom Windows PowerShell Desired State Configuration Resources

Nota

Las colecciones genéricas no se admiten en recursos basados en clases.

Estructura de carpetas de un recurso de clase

Para implementar un recurso personalizado de DSC con una clase de PowerShell, cree la siguiente estructura de carpetas. La clase se define en MyDscResource.psm1 y el manifiesto del módulo se define en MyDscResource.psd1.

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

Creación de la clase

Use la palabra clave class para crear una clase de PowerShell. Para especificar que una clase es un recurso de DSC, use el atributo DscResource(). El nombre de la clase es el nombre del recurso de DSC.

[DscResource()]
class NewFile {
}

Declarar propiedades

El esquema de recursos de DSC se define como propiedades de la clase . Declaramos tres propiedades como se indica a continuación.

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

Observe que los atributos modifican las propiedades. El significado de los atributos es el siguiente:

  • DscProperty(Key): se requiere la propiedad . La propiedad es una clave. Los valores de todas las propiedades marcadas como claves deben combinarse para identificar de forma única una instancia de recurso dentro de una configuración.
  • DscProperty(Obligatorio): se requiere la propiedad .
  • DscProperty(NotConfigurable): la propiedad es de solo lectura. Las propiedades marcadas con este atributo no se pueden establecer mediante una configuración, pero se rellenan mediante el método Get() cuando están presentes.
  • DscProperty(): la propiedad es configurable, pero no es necesaria.

Las propiedades $Path y $SourcePath son cadenas. El es una propiedad DateTime. La propiedad $Ensure es un tipo de enumeración, definido como se indica a continuación.

enum Ensure
{
    Absent
    Present
}

Inserción de clases

Si desea incluir un nuevo tipo con propiedades definidas que puede usar en el recurso, solo tiene que crear una clase con tipos de propiedad como se describió anteriormente.

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

    [DscProperty()]
    [string] $Phrase
}

Nota

La clase MyDscResourceReason se declara aquí con el nombre del módulo como prefijo. Aunque puede asignar cualquier nombre a las clases incrustadas, si dos o más módulos definen una clase con el mismo nombre y se usan en una configuración, PowerShell genera una excepción.

Para evitar excepciones causadas por conflictos de nombres en DSC, prefijo los nombres de las clases insertadas con el nombre del módulo. Si no es probable que el nombre de la clase insertada entre en conflicto, puede usarlo sin prefijo.

Si el recurso de DSC está diseñado para su uso con la característica de configuración de la máquina de Azure Automanage, siempre prefijo el nombre de la clase incrustada que cree para la propiedad Reasons.

Funciones públicas y privadas

Puede crear funciones de PowerShell en el mismo archivo de módulo y usarlas dentro de los métodos del recurso de clase DSC. Las funciones deben declararse como públicas, pero los bloques de script dentro de esas funciones públicas pueden llamar a funciones privadas. La única diferencia es si se muestran en la propiedad FunctionsToExport del manifiesto del módulo.

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

Implementación de los métodos

Los métodos Get(), Set()y Test() son análogos a las funciones Get-TargetResource, Set-TargetResourcey Test-TargetResource en un recurso de script.

Como procedimiento recomendado, minimice la cantidad de código dentro de la implementación de la clase. En su lugar, mueva la mayoría del código a las funciones públicas del módulo, que luego se pueden probar de forma independiente.

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

Archivo completo

A continuación se muestra el archivo de clase 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
    }
}

Creación de un manifiesto

Para que un recurso basado en clases esté disponible para el motor de DSC, debe incluir una instrucción DscResourcesToExport en el archivo de manifiesto que indique al módulo que exporte el recurso. Nuestro manifiesto tiene este aspecto:

@{

    # 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

    }
}

Prueba del recurso

Después de guardar los archivos de clase y manifiesto en la estructura de carpetas como se ha descrito anteriormente, puede crear una configuración que use el nuevo recurso. Para obtener información sobre cómo ejecutar una configuración de DSC, consulte Configuración de la creación de. La siguiente configuración comprobará si el archivo en /tmp/test.txt existe y si el contenido coincide con la cadena proporcionada por la propiedad "Content". Si no es así, se escribe todo el archivo.

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

Compatibilidad con PsDscRunAsCredential

[Nota] psDscRunAsCredential se admite en PowerShell 5.0 y versiones posteriores.

La propiedad psDscRunAsCredential se puede usar en configuraciones de DSC bloque de recursos para especificar que el recurso debe ejecutarse en un conjunto especificado de credenciales. Para obtener más información, consulte Ejecución de DSC con credenciales de usuario.

Requerir o no permitir PsDscRunAsCredential para el recurso

El atributo DscResource() toma un parámetro opcional runAsCredential. Este parámetro toma uno de los tres valores:

  • Optional psDscRunAsCredential es opcional para las configuraciones que llaman a este recurso. Este es el valor predeterminado.
  • Mandatory psDscRunAsCredential debe usarse para cualquier configuración que llame a este recurso.
  • NotSupported Configuraciones que llaman a este recurso no pueden usar PsDscRunAsCredential.
  • Default igual que Optional.

Por ejemplo, use el siguiente atributo para especificar que el recurso personalizado no admite el uso de PsDscRunAsCredential:

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

Declarar varios recursos de clase en un módulo

Un módulo puede definir varios recursos de DSC basados en clases. Solo tiene que declarar todas las clases en el mismo archivo .psm1 e incluir cada nombre en el manifiesto de .psd1.

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

Acceso al contexto del usuario

Para acceder al contexto de usuario desde un recurso personalizado, puede usar la variable automática $global:PsDscContext.

Por ejemplo, el código siguiente escribiría el contexto de usuario en el que se ejecuta el recurso en el flujo de salida detallado:

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

Consulte también

crear recursos personalizados de configuración de estado deseado de Windows PowerShell