다음을 통해 공유


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 리소스에 대한 자세한 내용은 Build Custom Windows PowerShell Desired State Configuration Resources(사용자 지정 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(Mandatory) : 이 속성은 필수입니다.
  • DscProperty(NotConfigurable) : 속성이 읽기 전용입니다. 이 특성으로 표시된 속성은 구성으로 설정할 수 없지만 존재할 경우 Get() 메서드로 채워집니다.
  • DscProperty() : 이 속성은 구성이 가능하지만 필수는 아닙니다.

$Path$SourcePath 속성은 모두 문자열입니다. $CreationTimeDateTime 속성입니다. $Ensure 속성은 다음과 같이 정의하는 열거형 형식입니다.

enum Ensure
{
    Absent
    Present
}

클래스 포함

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

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

    [DscProperty()]
    [string] $Phrase
}

참고

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

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

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

퍼블릭 및 프라이빗 함수

동일한 모듈 파일에 PowerShell 함수를 만들어 DSC 클래스 리소스의 메서드에서 사용할 수 있습니다. 함수는 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
}

메서드 구현

The 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을 필수 항목으로 지정하거나 사용 차단

DscResource() 특성은 선택적 매개 변수 RunAsCredential을 사용합니다. 이 매개 변수는 다음의 3개 값 중 하나를 사용합니다.

  • OptionalPsDscRunAsCredential 은 이 리소스를 호출하는 구성에 대해 선택 사항입니다. 이것은 기본값입니다.
  • MandatoryPsDscRunAsCredential은 이 리소스를 호출하는 모든 구성에 사용해야 합니다.
  • 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 필요한 상태 구성 리소스 빌드