다음을 통해 공유


PowerShell 클래스를 사용하여 사용자 지정 DSC 리소스 작성

적용 대상: Windows PowerShell 5.0

Windows PowerShell 5.0에서 PowerShell 클래스가 도입되면서 이제 클래스를 만들어 DSC 리소스를 정의할 수 있습니다. 클래스는 리소스의 스키마와 구현을 모두 정의하므로 별도의 MOF 파일을 만들 필요가 없습니다. DSCResources 폴더가 필요하지 않으므로 클래스 기반 리소스의 폴더 구조도 더 간단합니다.

클래스 기반 DSC 리소스에서 스키마는 속성 형식을 지정하기 위해 특성을 사용하여 수정할 수 있는 클래스의 속성으로 정의됩니다. 리소스는 스크립트 리소스의 Get(), Set()Test() 메서드(Get-TargetResource, Set-TargetResourceTest-TargetResource 함수에 따라 구현됩니다.

이 문서에서는 지정된 경로의 파일을 관리하는 NewFile이라는 간단한 리소스를 만듭니다.

DSC 리소스에 대한 자세한 내용은 사용자 지정 Windows PowerShell 필요한 상태 구성 리소스 빌드 참조하세요.

메모

제네릭 컬렉션은 클래스 기반 리소스에서 지원되지 않습니다.

클래스 리소스의 폴더 구조

PowerShell 클래스를 사용하여 DSC 사용자 지정 리소스를 구현하려면 다음 폴더 구조를 만듭니다. 클래스는 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 속성은 모두 문자열입니다. $CreationTime DateTime 속성입니다. $Ensure 속성은 다음과 같이 정의된 열거형 형식입니다.

enum Ensure
{
    Absent
    Present
}

클래스 포함

리소스 내에서 사용할 수 있는 정의된 속성이 포함된 새 형식을 포함하려면 위에서 설명한 대로 속성 형식이 있는 클래스를 만드세요.

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

    [DscProperty()]
    [string] $Phrase
}

메모

MyDscResourceReason 클래스는 여기에서 모듈의 이름을 접두사로 선언합니다. 포함된 클래스에 이름을 지정할 수 있지만 두 개 이상의 모듈이 동일한 이름의 클래스를 정의하고 모두 구성에 사용되는 경우 PowerShell에서 예외가 발생합니다.

DSC에서 이름 충돌로 인한 예외를 방지하려면 포함된 클래스의 이름 앞에 모듈 이름을 접두사로 추가합니다. 포함된 클래스의 이름이 이미 충돌할 가능성이 낮으면 접두사 없이 사용할 수 있습니다.

DSC 리소스가 Azure Automanage의 컴퓨터 구성 기능과 함께 사용하도록 설계된 경우 항상 Reasons 속성에 대해 만든 포함된 클래스의 이름을 접두사로 지정합니다.

공용 및 프라이빗 함수

동일한 모듈 파일 내에서 PowerShell 함수를 만들고 DSC 클래스 리소스의 메서드 내에서 사용할 수 있습니다. 함수는 public으로 선언해야 하지만 해당 public 함수 내의 스크립트 블록은 프라이빗 함수를 호출할 수 있습니다. 유일한 차이점은 모듈 매니페스트의 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-TargetResource, Set-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 파일이 있는지 여부와 콘텐츠가 'Content' 속성에서 제공하는 문자열과 일치하는지 확인합니다. 그렇지 않은 경우 전체 파일이 기록됩니다.

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 필요 또는 허용 불허

특성은 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 필요한 상태 구성 리소스 빌드