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 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 Get(), , y Test() los métodos (equivalentes a , , y Test-TargetResource funcionan en un recurso de Get-TargetResourceSet-TargetResourceSet()script.

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

Para obtener más información sobre los recursos de DSC, consulte Compilación de recursos de configuración de estado deseado personalizados de Windows PowerShell

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 DscResource() atributo. 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): la propiedad es necesaria. 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(Mandatory): la propiedad es necesaria.
  • DscProperty(NotConfigurable): la propiedad es de solo lectura. Las propiedades marcadas con este atributo no se pueden establecer mediante una configuración, sino que el método las rellena Get() cuando están presentes.
  • DscProperty(): la propiedad se puede configurar, pero no es necesaria.

Las $Path propiedades and $SourcePath son cadenas. Es $CreationTime una propiedad DateTime . La $Ensure propiedad es un tipo de enumeración, definida de la siguiente manera.

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 MyDscResourceReason clase 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 configuración de la máquina de Azure, prefije siempre 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 enumeran en la FunctionsToExport propiedad 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 Get()métodos , Set(), y Test() son análogos a las Get-TargetResourcefunciones , Set-TargetResource, y 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 DscResourcesToExport instrucción 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 Promulgación de configuraciones. La siguiente configuración comprobará si el archivo at /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 el bloque de recursos de configuración de DSC para especificar que el recurso debe ejecutarse con 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 DscResource() atributo 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 se debe usar para cualquier configuración que llame a este recurso.
  • NotSupported Las 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 necesita declarar todas las clases en el mismo .psm1 archivo e incluir cada nombre en el .psd1 manifiesto.

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

Acceso al contexto del usuario

Para acceder al contexto del usuario desde un recurso personalizado, puede utilizar la variable $global:PsDscContextautomática .

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

Compilación de recursos de configuración de estado deseados personalizados de Windows PowerShell