كتابة مورد DSC مخصص باستخدام فئات PowerShell

ينطبق على: Windows PowerShell 5.0

مع إدخال فئات PowerShell في Windows PowerShell 5.0، يمكنك الآن تعريف مورد DSC عن طريق إنشاء فئة. تحدد الفئة كلا من المخطط وتنفيذ المورد، لذلك ليست هناك حاجة لإنشاء ملف MOF منفصل. بنية المجلد لمورد يستند إلى فئة أبسط أيضا، لأن DSCResources المجلد غير ضروري.

في مورد DSC المستند إلى الفئة، يتم تعريف المخطط كخصائص للفئة التي يمكن تعديلها باستخدام سمات لتحديد نوع الخاصية. يتم تنفيذ المورد بواسطة أساليب Get()Set()Test() (ما يعادل وظائف Get-TargetResourceSet-TargetResourceTest-TargetResource في مورد برنامج نصي.

في هذه المقالة، سننشئ موردا بسيطا يسمى NewFile يدير ملفا في مسار محدد.

لمزيد من المعلومات حول موارد DSC، راجع إنشاء موارد تكوين الحالة المطلوبة ل Windows PowerShell المخصصة

ملاحظه

المجموعات العامة غير معتمدة في الموارد المستندة إلى الفئة.

بنية المجلد لمورد فئة

لتنفيذ مورد مخصص DSC مع فئة PowerShell، قم بإنشاء بنية المجلد التالية. يتم تعريف الفئة في MyDscResource.psm1 ويتم تعريف بيان الوحدة النمطية في MyDscResource.psd1.

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

إنشاء الفئة

يمكنك استخدام الكلمة الأساسية للفئة لإنشاء فئة PowerShell. لتحديد أن الفئة هي مورد DSC، استخدم السمة DscResource(). اسم الفئة هو اسم مورد DSC.

[DscResource()]
class NewFile {
}

تعريف الخصائص

يتم تعريف مخطط مورد DSC كخصائص للفئة. نعلن عن ثلاث خصائص على النحو التالي.

[DscProperty(Key)]
[string] $path

[DscProperty(Mandatory)]
[ensure] $ensure

[DscProperty()]
[string] $content

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

لاحظ أن الخصائص يتم تعديلها بواسطة السمات. معنى السمات كما يلي:

  • DscProperty(Key): الخاصية مطلوبة. الخاصية هي مفتاح. يجب دمج قيم كافة الخصائص التي تم وضع علامة عليها كمفاتيح لتحديد مثيل مورد بشكل فريد داخل تكوين.
  • DscProperty(إلزامي): الخاصية مطلوبة.
  • DscProperty(NotConfigurable): الخاصية للقراءة فقط. لا يمكن تعيين الخصائص التي تم وضع علامة عليها بهذه السمة بواسطة تكوين، ولكن يتم ملؤها بواسطة أسلوب Get() عند وجودها.
  • DscProperty(): الخاصية قابلة للتكوين، ولكنها غير مطلوبة.

الخاصيتان $Path و $SourcePath هما سلسلتان. هو خاصية DateTime . الخاصية $Ensure هي نوع تعداد، معرف على النحو التالي.

enum Ensure
{
    Absent
    Present
}

تضمين الفئات

إذا كنت ترغب في تضمين نوع جديد مع خصائص محددة يمكنك استخدامها داخل المورد الخاص بك، فما عليك سوى إنشاء فئة مع أنواع الخصائص كما هو موضح أعلاه.

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

    [DscProperty()]
    [string] $Phrase
}

ملاحظه

يتم الإعلان عن فئة MyDscResourceReason هنا باسم الوحدة النمطية كبادئة. بينما يمكنك إعطاء الفئات المضمنة أي اسم، إذا حددت وحدتان أو أكثر فئة بنفس الاسم وتم استخدامهما في تكوين، فإن PowerShell يثير استثناء.

لتجنب الاستثناءات الناتجة عن تعارضات الأسماء في DSC، قم ببادئة أسماء الفئات المضمنة باسم الوحدة النمطية. إذا كان من غير المحتمل أن يتعارض اسم الفئة المضمنة بالفعل، يمكنك استخدامه دون بادئة.

إذا تم تصميم مورد DSC للاستخدام مع ميزة تكوين جهاز Azure Automanage، فبادر دائما ببادئة اسم الفئة المضمنة التي تقوم بإنشائها للخاصية أسباب.

الدالات العامة والخاصة

يمكنك إنشاء وظائف PowerShell داخل نفس ملف الوحدة النمطية واستخدامها داخل أساليب مورد فئة DSC. يجب الإعلان عن الدالات كدالات عامة، ولكن كتل البرنامج النصي داخل تلك الدالات العامة يمكن استدعاء الدالات الخاصة. الفرق الوحيد هو ما إذا كانت مدرجة في الخاصية FunctionsToExport لبيان الوحدة النمطية.

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

تنفيذ الأساليب

أساليب Get()Set()Test() مماثلة للدالات Get-TargetResourceSet-TargetResourceTest-TargetResource في مورد البرنامج النصي.

كأفضل ممارسة، قلل مقدار التعليمات البرمجية داخل تنفيذ الفئة. بدلا من ذلك، انقل غالبية التعليمات البرمجية الخاصة بك إلى الوظائف العامة في الوحدة النمطية، والتي يمكن اختبارها بعد ذلك بشكل مستقل.

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

الملف الكامل

يتبع ملف الفئة الكامل.

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

إنشاء بيان

لتوفير مورد يستند إلى الفئة لمحرك DSC، يجب تضمين عبارة DscResourcesToExport في ملف البيان الذي يوجه الوحدة النمطية لتصدير المورد. يبدو بياننا كما يلي:

@{

    # 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

    }
}

اختبار المورد

بعد حفظ ملفات الفئة والبيانات في بنية المجلد كما هو موضح سابقا، يمكنك إنشاء تكوين يستخدم المورد الجديد. للحصول على معلومات حول كيفية تشغيل تكوين DSC، راجع سن التكوينات. سيتحقق التكوين التالي لمعرفة ما إذا كان الملف في /tmp/test.txt موجودا وما إذا كانت المحتويات تطابق السلسلة التي توفرها الخاصية "المحتوى". إذا لم يكن الأمر كما هو، تتم كتابة الملف بأكمله.

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

دعم PsDscRunAsCredential

[ملاحظة] يتم دعم PsDscRunAsCredential في PowerShell 5.0 والإحدث.

يمكن استخدام خاصية PsDscRunAsCredential في تكوينات DSC كتلة الموارد لتحديد أنه يجب تشغيل المورد ضمن مجموعة محددة من بيانات الاعتماد. لمزيد من المعلومات، راجع تشغيل DSC مع بيانات اعتماد المستخدم.

طلب PsDscRunAsCredential لموردك أو عدم السماح به

تأخذ السمة DscResource() معلمة اختيارية RunAsCredential. تأخذ هذه المعلمة إحدى القيم الثلاث:

  • Optional PsDscRunAsCredential اختياري للتكوينات التي تستدعي هذا المورد. هذه هي القيمة الافتراضية.
  • Mandatory يجب استخدام PsDscRunAsCredential لأي تكوين يستدعي هذا المورد.
  • لا يمكن NotSupported التكوينات التي تستدعي هذا المورد استخدام PsDscRunAsCredential.
  • Default نفس Optional.

على سبيل المثال، استخدم السمة التالية لتحديد أن المورد المخصص لا يدعم استخدام PsDscRunAsCredential:

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

الإعلان عن موارد فئة متعددة في وحدة نمطية

يمكن للوحدة النمطية تحديد موارد DSC متعددة تستند إلى الفئة. تحتاج فقط إلى تعريف جميع الفئات في نفس ملف .psm1 وتضمين كل اسم في بيان .psd1.

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

الوصول إلى سياق المستخدم

للوصول إلى سياق المستخدم من داخل مورد مخصص، يمكنك استخدام المتغير التلقائي $global:PsDscContext.

على سبيل المثال، ستكتب التعليمات البرمجية التالية سياق المستخدم الذي يتم تشغيل المورد ضمنه إلى دفق الإخراج المطول:

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

راجع أيضا

إنشاء موارد تكوين الحالة المطلوبة ل Windows PowerShell المخصصة