Bagikan melalui


Menulis sumber daya DSC kustom dengan kelas PowerShell

Berlaku Untuk: Windows PowerShell 5.0

Dengan pengenalan kelas PowerShell di Windows PowerShell 5.0, Anda sekarang dapat menentukan sumber daya DSC dengan membuat kelas. Kelas mendefinisikan skema dan implementasi sumber daya, sehingga tidak perlu membuat file MOF terpisah. Struktur folder untuk sumber daya berbasis kelas juga lebih sederhana, karena folder DSCResources tidak diperlukan.

Dalam sumber daya DSC berbasis kelas, skema didefinisikan sebagai properti kelas yang dapat dimodifikasi dengan atribut untuk menentukan jenis properti. Sumber daya diimplementasikan oleh Get()metode , Set(), dan Test() (setara Get-TargetResourcedengan fungsi , , Set-TargetResourcedan Test-TargetResource dalam sumber daya skrip.

Dalam artikel ini, kita akan membuat sumber daya sederhana bernama NewFile yang mengelola file di jalur tertentu.

Untuk informasi selengkapnya tentang sumber daya DSC, lihat Membangun Sumber Daya Konfigurasi Status yang Diinginkan Windows PowerShell Kustom

Catatan

Koleksi generik tidak didukung dalam sumber daya berbasis kelas.

Struktur folder untuk sumber daya kelas

Untuk mengimplementasikan sumber daya kustom DSC dengan kelas PowerShell, buat struktur folder berikut. Kelas didefinisikan dalam MyDscResource.psm1 dan manifes modul didefinisikan dalam MyDscResource.psd1.

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

Membuat kelas

Anda menggunakan kata kunci kelas untuk membuat kelas PowerShell. Untuk menentukan bahwa kelas adalah sumber daya DSC, gunakan DscResource() atribut . Nama kelas adalah nama sumber daya DSC.

[DscResource()]
class NewFile {
}

Mendeklarasikan properti

Skema sumber daya DSC didefinisikan sebagai properti kelas . Kami menyatakan tiga properti sebagai berikut.

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

Perhatikan bahwa properti dimodifikasi oleh atribut. Arti atribut adalah sebagai berikut:

  • DscProperty(Key): Properti diperlukan. Properti adalah kunci. Nilai semua properti yang ditandai sebagai kunci harus digabungkan untuk mengidentifikasi instans sumber daya secara unik dalam konfigurasi.
  • DscProperty(Wajib): Properti diperlukan.
  • DscProperty(NotConfigurable): Properti bersifat baca-saja. Properti yang ditandai dengan atribut ini tidak dapat diatur oleh konfigurasi, tetapi diisi oleh Get() metode saat ada.
  • DscProperty(): Properti dapat dikonfigurasi, tetapi tidak diperlukan.

Properti $Path dan $SourcePath keduanya adalah string. $CreationTime adalah properti DateTime. Properti $Ensure adalah jenis enumerasi, didefinisikan sebagai berikut.

enum Ensure
{
    Absent
    Present
}

Menyematkan kelas

Jika Anda ingin menyertakan jenis baru dengan properti yang ditentukan yang dapat Anda gunakan dalam sumber daya Anda, cukup buat kelas dengan jenis properti seperti yang dijelaskan di atas.

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

    [DscProperty()]
    [string] $Phrase
}

Catatan

Kelas MyDscResourceReason dinyatakan di sini dengan nama modul sebagai awalan. Meskipun Anda dapat memberikan nama apa pun pada kelas yang disematkan, jika dua modul atau lebih menentukan kelas dengan nama yang sama dan keduanya digunakan dalam konfigurasi, PowerShell akan memunculkan pengecualian.

Untuk menghindari pengecualian yang disebabkan oleh konflik nama di DSC, awali nama kelas tersemat Anda dengan nama modul. Jika nama kelas tersemat Anda sudah tidak mungkin bertentangan, Anda dapat menggunakannya tanpa awalan.

Jika Sumber Daya DSC Anda dirancang untuk digunakan dengan fitur konfigurasi mesin Azure Automanage, selalu awali nama kelas tersemat yang Anda buat untuk properti Reasons .

Fungsi Publik dan Privat

Anda dapat membuat fungsi PowerShell dalam file modul yang sama dan menggunakannya di dalam metode sumber daya kelas DSC Anda. Fungsi harus dinyatakan sebagai publik, namun blok skrip dalam fungsi publik tersebut dapat memanggil fungsi yang bersifat privat. Satu-satunya perbedaan adalah apakah mereka tercantum dalam FunctionsToExport properti manifes modul.

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

Menerapkan metode

Metode Get(), Set(), dan Test() dianalogikan dengan Get-TargetResourcefungsi , Set-TargetResource, dan Test-TargetResource dalam sumber daya skrip.

Sebagai praktik terbaik, minimalkan jumlah kode dalam implementasi kelas. Sebagai gantinya, pindahkan sebagian besar kode Anda ke fungsi publik dalam modul, yang kemudian dapat diuji secara independen.

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

File kelas lengkap mengikuti.

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

Membuat manifes

Untuk membuat sumber daya berbasis kelas tersedia untuk mesin DSC, Anda harus menyertakan DscResourcesToExport pernyataan dalam file manifes yang menginstruksikan modul untuk mengekspor sumber daya. Manifes kami terlihat seperti ini:

@{

    # 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

    }
}

Menguji sumber daya

Setelah menyimpan file kelas dan manifes dalam struktur folder seperti yang dijelaskan sebelumnya, Anda dapat membuat konfigurasi yang menggunakan sumber daya baru. Untuk informasi tentang cara menjalankan konfigurasi DSC, lihat Memberlakukan konfigurasi. Konfigurasi berikut akan memeriksa untuk melihat apakah file ada /tmp/test.txt dan apakah konten cocok dengan string yang disediakan oleh properti 'Konten'. Jika tidak, seluruh file ditulis.

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

Mendukung PsDscRunAsCredential

[Catatan] PsDscRunAsCredential didukung di PowerShell 5.0 dan yang lebih baru.

Properti PsDscRunAsCredential dapat digunakan dalam blok sumber daya konfigurasi DSC untuk menentukan bahwa sumber daya harus dijalankan di bawah sekumpulan kredensial yang ditentukan. Untuk informasi selengkapnya, lihat Menjalankan DSC dengan kredensial pengguna.

Memerlukan atau melarang PsDscRunAsCredential untuk sumber daya Anda

Atribut DscResource() mengambil parameter opsional RunAsCredential. Parameter ini mengambil salah satu dari tiga nilai:

  • OptionalPsDscRunAsCredential bersifat opsional untuk konfigurasi yang memanggil sumber daya ini. Ini adalah nilai default.
  • MandatoryPsDscRunAsCredential harus digunakan untuk konfigurasi apa pun yang memanggil sumber daya ini.
  • NotSupported Konfigurasi yang memanggil sumber daya ini tidak dapat menggunakan PsDscRunAsCredential.
  • Default Sama seperti Optional.

Misalnya, gunakan atribut berikut untuk menentukan bahwa sumber daya kustom Anda tidak mendukung penggunaan PsDscRunAsCredential:

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

Mendeklarasikan beberapa sumber daya kelas dalam modul

Modul dapat menentukan beberapa sumber daya DSC berbasis kelas. Anda hanya perlu mendeklarasikan semua kelas dalam file yang sama .psm1 dan menyertakan setiap nama dalam .psd1 manifes.

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

Mengakses konteks pengguna

Untuk mengakses konteks pengguna dari dalam sumber daya kustom, Anda dapat menggunakan variabel $global:PsDscContextotomatis .

Misalnya kode berikut akan menulis konteks pengguna di mana sumber daya berjalan ke aliran output verbose:

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

Lihat juga

Membangun Sumber Daya Konfigurasi Status yang Diinginkan Windows PowerShell Kustom