다음을 통해 공유


about_Classes_and_DSC

간단한 설명

클래스를 사용하여 DSC(Desired State Configuration)를 사용하여 PowerShell에서 개발하는 방법을 설명합니다.

자세한 설명입니다.

Windows PowerShell 5.0부터 다른 개체 지향 프로그래밍 언어와 유사한 공식 구문 및 의미 체계를 사용하여 클래스 및 기타 사용자 정의 형식을 정의하는 언어가 추가되었습니다. 목표는 개발자와 IT 전문가가 광범위한 사용 사례에 대해 PowerShell을 수용하고, DSC 리소스와 같은 PowerShell 아티팩트 개발을 간소화하고, 관리 화면의 적용 범위를 가속화할 수 있도록 하는 것입니다.

지원되는 시나리오

다음과 같은 시나리오가 지원됩니다.

  • PowerShell 언어를 사용하여 DSC 리소스 및 관련 형식을 정의합니다.
  • 클래스, 속성, 메서드 및 상속과 같은 친숙한 개체 지향 프로그래밍 구문을 사용하여 PowerShell에서 사용자 지정 형식을 정의합니다.
  • PowerShell 언어를 사용하여 형식을 디버그합니다.
  • 형식 메커니즘을 사용하여 적절한 수준에서 예외를 생성하고 처리합니다.

클래스를 사용하여 DSC 리소스 정의

구문 변경 외에도 클래스 정의 DSC 리소스와 cmdlet DSC 리소스 공급자 간의 주요 차이점은 다음과 같습니다.

  • MOF(관리 개체 형식) 파일은 필요하지 않습니다.
  • 모듈 폴더에서 DSCResource 하위 폴더가 필요하지 않습니다.
  • PowerShell 모듈 파일에 여러 가지 DSC 리소스 클래스가 포함될 수 있습니다.

클래스 정의 DSC 리소스 공급자 만들기

다음 예제는 모듈인 MyDSCResource.psm1로 저장되는 클래스 정의 DSC 리소스 공급자입니다. 항상 클래스 정의 DSC 리소스 공급자에 키 속성을 포함해야 합니다.

enum Ensure
{
    Absent
    Present
}

<#
    This resource manages the file in a specific path.
    [DscResource()] indicates the class is a DSC resource
#>

[DscResource()]
class FileResource
{
    <#
        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 defines the fully qualified path to a file that will
        be placed on the system if $Ensure = Present and $Path does not
        exist.

        NOTE: This property is required because [DscProperty(Mandatory)] is
        set.
    #>
    [DscProperty(Mandatory)]
    [string] $SourcePath

    <#
        This property reports the file's create timestamp.

        [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)]
    [Nullable[datetime]] $CreationTime

    <#
        This method is equivalent of the Set-TargetResource script function.
        It sets the resource to the desired state.
    #>
    [void] Set()
    {
        $fileExists = $this.TestFilePath($this.Path)
        if($this.ensure -eq [Ensure]::Present)
        {
            if(-not $fileExists)
            {
                $this.CopyFile()
            }
        }
        else
        {
            if($fileExists)
            {
                Write-Verbose -Message "Deleting the file $($this.Path)"
                Remove-Item -LiteralPath $this.Path -Force
            }
        }
    }

    <#

        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()
    {
        $present = $this.TestFilePath($this.Path)

        if($this.Ensure -eq [Ensure]::Present)
        {
            return $present
        }
        else
{
            return -not $present
        }
    }

    <#
        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.
    #>
    [FileResource] Get()
    {
        $present = $this.TestFilePath($this.Path)

        if ($present)
        {
            $file = Get-ChildItem -LiteralPath $this.Path
            $this.CreationTime = $file.CreationTime
            $this.Ensure = [Ensure]::Present
        }
        else
        {
            $this.CreationTime = $null
            $this.Ensure = [Ensure]::Absent
        }
        return $this
    }

    <#
        Helper method to check if the file exists and it is correct file
    #>
    [bool] TestFilePath([string] $location)
    {
        $present = $true

        $item = Get-ChildItem -LiteralPath $location -ea Ignore
        if ($null -eq $item)
        {
            $present = $false
        }
        elseif( $item.PSProvider.Name -ne "FileSystem")
        {
            throw "Path $($location) is not a file path."
        }
        elseif($item.PSIsContainer)
        {
            throw "Path $($location) is a directory path."
        }
        return $present
    }

    <#
        Helper method to copy file from source to path
    #>
    [void] CopyFile()
    {
        if(-not $this.TestFilePath($this.SourcePath))
        {
            throw "SourcePath $($this.SourcePath) is not found."
        }

        [System.IO.FileInfo]
        $destFileInfo = new-object System.IO.FileInfo($this.Path)

        if (-not $destFileInfo.Directory.Exists)
        {
            $FullName = $destFileInfo.Directory.FullName
            $Message = "Creating directory $FullName"

            Write-Verbose -Message $Message

            #use CreateDirectory instead of New-Item to avoid code
            # to handle the non-terminating error
            [System.IO.Directory]::CreateDirectory($FullName)
        }

        if(Test-Path -LiteralPath $this.Path -PathType Container)
        {
            throw "Path $($this.Path) is a directory path"
        }

        Write-Verbose -Message "Copying $this.SourcePath to $this.Path"

        #DSC engine catches and reports any error that occurs
        Copy-Item -Path $this.SourcePath -Destination $this.Path -Force
    }
}

모듈 매니페스트 만들기

클래스 정의 DSC 리소스 공급자를 만들고 모듈로 저장한 후 모듈에 대한 모듈 매니페스트를 만듭니다. DSC 엔진에 사용할 수 있는 클래스 기반 리소스를 만들려면 매니페스트 파일에 리소스를 내보내도록 모듈에게 지시하는 DscResourcesToExport 문을 포함해야 합니다. 이 예제에서는 다음 모듈 매니페스트가 MyDscResource.psd1로 저장됩니다.

@{

# Script module or binary module file associated with this manifest.
RootModule = 'MyDscResource.psm1'

DscResourcesToExport = 'FileResource'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '81624038-5e71-40f8-8905-b1a87afe22d7'

# Author of this module
Author = 'Microsoft Corporation'

# Company or vendor of this module
CompanyName = 'Microsoft Corporation'

# Copyright statement for this module
Copyright = '(c) 2014 Microsoft. All rights reserved.'

# Description of the functionality provided by this module
# Description = ''

# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.0'

# Name of the PowerShell host required by this module
# PowerShellHostName = ''

}

DSC 리소스 공급자 배포

내 또는 $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules.에 MyDscResource 폴더를 만들어 새 DSC 리소스 공급자를 $pshome\Modules 배포합니다.

DSCResource 하위 폴더는 만들 필요가 없습니다. 모듈 및 모듈 매니페스트 파일(MyDscResource.psm1 및 MyDscResource.psd1)을 MyDscResource 폴더에 복사합니다.

이 시점에서 DSC 리소스와 마찬가지로 구성 스크립트를 만들어 실행합니다.

DSC 구성 스크립트 만들기

앞에서 설명한 대로 폴더 구조에 클래스 및 매니페스트 파일을 저장한 후에는 새 리소스를 사용하는 구성을 만들 수 있습니다. 다음 구성은 MyDSCResource 모듈을 참조합니다. MyResource.ps1 스크립트로 구성을 저장합니다.

DSC 구성을 실행하는 방법에 대한 자세한 내용은 Windows PowerShell Desired State Configuration 개요를 참조하세요.

구성을 실행하기 전에 만듭니다 C:\test.txt. 구성은 파일이 .에 c:\test\test.txt있는지 확인합니다. 파일이 없으면 구성에서 파일을 C:\test.txt복사합니다.

Configuration Test
{
    Import-DSCResource -ModuleName MyDscResource
    FileResource file
    {
        Path = "C:\test\test.txt"
        SourcePath = "C:\test.txt"
        Ensure = "Present"
    }
}
Test
Start-DscConfiguration -Wait -Force Test

DSC 구성 스크립트와 마찬가지로 이 스크립트를 실행합니다. 구성을 시작하려면 관리자 권한 PowerShell 콘솔에서 다음을 실행합니다.

PS C:\test> .\MyResource.ps1

PowerShell 클래스의 상속

PowerShell 클래스에 대한 기본 클래스 선언

다음 예제와 같이 과일이 사과의 기본 형식인 PowerShell 클래스를 다른 PowerShell 클래스의 기본 형식으로 선언할 수 있습니다.

class fruit
{
    [int]sold() {return 100500}
}

class apple : fruit {}
    [apple]::new().sold() # return 100500

PowerShell 클래스에 대해 구현된 인터페이스 선언

기본 형식 다음에 구현된 인터페이스를 선언하거나, 기본 형식이 지정되지 않은 경우 콜론(:) 바로 다음에 선언할 수 있습니다. 쉼표를 사용하여 모든 형식 이름을 구분합니다. 이는 C# 구문과 유사합니다.

class MyComparable : system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

class MyComparableTest : test, system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

기본 클래스 생성자 호출

하위 클래스에서 기본 클래스 생성자를 호출하려면 다음 예제와 같이 키워드를 추가 base 합니다.

class A {
    [int]$a
    A([int]$a)
    {
        $this.a = $a
    }
}

class B : A
{
    B() : base(103) {}
}

    [B]::new().a # return 103

기본 클래스에 기본 생성자(매개 변수 없음)가 있는 경우 표시된 것처럼 명시적 생성자 호출을 생략할 수 있습니다.

class C : B
{
    C([int]$c) {}
}

기본 클래스 메서드 호출

하위 클래스에서 기존 메서드를 재정의할 수 있습니다. 재정의를 수행하려면 동일한 이름과 서명을 사용하여 메서드를 선언합니다.

class baseClass
{
    [int]days() {return 100500}
}
class childClass1 : baseClass
{
    [int]days () {return 200600}
}

    [childClass1]::new().days() # return 200600

재정의된 구현에서 기본 클래스 메서드를 호출하려면 호출 시 기본 클래스 ([baseclass]$this) 로 캐스팅합니다.

class childClass2 : baseClass
{
    [int]days()
    {
        return 3 * ([baseClass]$this).days()
    }
}

    [childClass2]::new().days() # return 301500

모든 PowerShell 메서드는 가상입니다. 재정의와 동일한 구문을 사용하여 하위 클래스에서 가상이 아닌 .NET 메서드를 숨길 수 있습니다. 동일한 이름과 서명이 있는 메서드를 선언합니다.

class MyIntList : system.collections.generic.list[int]
{
    # Add is final in system.collections.generic.list
    [void] Add([int]$arg)
    {
        ([system.collections.generic.list[int]]$this).Add($arg * 2)
    }
}

$list = [MyIntList]::new()
$list.Add(100)
$list[0] # return 200

클래스 상속의 현재 제한 사항

클래스 상속의 제한 사항은 PowerShell에서 인터페이스를 선언하는 구문이 없다는 것입니다.

PowerShell에서 사용자 지정 형식 정의

Windows PowerShell 5.0에는 여러 언어 요소가 도입되었습니다.

Class 키워드

새 클래스를 정의합니다. class 키워드는 true .NET Framework 형식입니다. 클래스 멤버는 공용입니다.

class MyClass
{
}

Enum 키워드 및 열거형

키워드에 enum 대한 지원이 추가되었으며 호환성이 손상되는 변경입니다. 구분 기호는 enum 현재 줄 바꿈입니다. 이미 사용 enum 중인 사용자를 위한 해결 방법은 단어 앞에 앰퍼샌드(&)를 삽입하는 것입니다. 현재 제한 사항: 자체적으로 열거자를 정의할 수는 없지만 다음 예제와 같이 다른 enum측면에서 초기화 enum 할 수 있습니다.

현재 기본 형식을 지정할 수 없습니다. 기본 형식은 항상 [int]입니다.

enum Color2
{
    Yellow = [Color]::Blue
}

열거자 값은 구문 분석 시간 상수여야 합니다. 열거자 값은 호출된 명령의 결과로 설정할 수 없습니다.

enum MyEnum
{
    Enum1
    Enum2
    Enum3 = 42
    Enum4 = [int]::MaxValue
}

Enum 는 다음 예제와 같이 산술 연산을 지원합니다.

enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }

숨겨진 키워드

Windows PowerShell 5.0에 도입된 키워드는 hidden 기본 Get-Member 결과에서 클래스 멤버를 숨깁니다. 다음 줄과 같이 숨겨진 속성을 지정합니다.

hidden [type] $classmember = <value>

숨겨진 멤버를 정의하는 클래스에서 완료가 발생하지 않는 한 숨겨진 멤버는 탭 완성 또는 IntelliSense를 사용하여 표시되지 않습니다.

C# 코드가 PowerShell 내에서 동일한 의미 체계를 가질 수 있도록 새 특성인 System.Management.Automation.HiddenAttribute가 추가되었습니다.

자세한 내용은 [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden)를 참조하세요.

Import-DscResource

Import-DscResource는 이제 진정한 동적 키워드입니다. PowerShell은 지정된 모듈의 루트 모듈을 구문 분석하여 DscResource 특성이 포함된 클래스를 검색합니다.

속성

에 새 필드가 ImplementingAssembly추가 ModuleInfo되었습니다. 스크립트가 클래스를 정의하거나 이진 모듈에 대해 로드된 어셈블리가 스크립트 모듈 ImplementingAssembly 에 대해 생성된 동적 어셈블리로 설정된 경우 ModuleType = Manifest인 경우에는 설정되지 않습니다.

필드에 대한 리플렉션은 ImplementingAssembly 모듈의 리소스를 검색합니다. 즉, PowerShell이나 다른 관리 언어로 작성된 리소스를 검색할 수 있습니다.

이니셜라이저가 있는 필드입니다.

[int] $i = 5

정적은 지원되며 형식 제약 조건과 유사한 특성처럼 작동하므로 임의의 순서로 지정할 수 있습니다.

static [int] $count = 0

형식은 선택 사항입니다.

$s = "hello"

모든 멤버는 공용입니다. 속성에는 줄 바꿈이나 세미콜론이 필요합니다. 개체 형식이 지정되지 않은 경우 속성 형식은 Object입니다.

생성자 및 인스턴스화

PowerShell 클래스에는 클래스와 이름이 같은 생성자가 있을 수 있습니다. 생성자는 오버로드할 수 있습니다. 정적 생성자가 지원됩니다. 초기화 식이 있는 속성은 생성자에서 코드를 실행하기 전에 초기화됩니다. 정적 속성은 정적 생성자의 본문보다 먼저 초기화되고, 인스턴스 속성은 비정적 생성자의 본문보다 먼저 초기화됩니다. 현재 C# 구문과 같은 다른 생성자에서 생성자를 호출하는 구문 ": this()")은 없습니다. 해결 방법은 일반적인 Init 메서드를 정의하는 것입니다.

클래스를 인스턴스화하는 방법은 다음과 같습니다.

  • 기본 생성자를 사용하여 인스턴스화합니다. New-Object 이 릴리스에서는 지원되지 않습니다.

    $a = [MyClass]::new()

  • 매개 변수를 사용하여 생성자 호출

    $b = [MyClass]::new(42)

  • 매개 변수가 여러 개인 생성자에 배열을 전달합니다.

    $c = [MyClass]::new(@(42,43,44), "Hello")

이 릴리스의 경우 형식 이름은 어휘적으로만 표시되므로 클래스를 정의하는 모듈 또는 스크립트 외부에서는 표시되지 않습니다. 함수는 PowerShell에 정의된 클래스의 인스턴스를 반환할 수 있으며 인스턴스는 모듈 또는 스크립트 외부에서 잘 작동합니다.

정적 매개 변수는 Get-Member 생성자를 나열하므로 다른 메서드와 마찬가지로 오버로드를 볼 수 있습니다. 이 구문의 성능 또한 New-Object보다 현저하게 빠릅니다.

new라는 의사 정적 메서드는 다음 예제와 같이 .NET 형식에서 작동합니다. [hashtable]::new()

이제 Get-Member를 사용하거나 다음 예제와 같이 생성자 오버로드를 확인할 수 있습니다.

[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)

메서드

PowerShell 클래스 메서드는 끝 블록만 있는 ScriptBlock으로 구현됩니다. 모든 메서드는 공용입니다. 다음에서는 DoSomething이라는 메서드를 정의하는 예제를 보여 줍니다.

class MyClass
{
    DoSomething($x)
    {
        $this._doSomething($x)       # method syntax
    }
    private _doSomething($a) {}
}

메서드 호출

오버로드된 메서드가 지원됩니다. 오버로드된 메서드의 이름은 기존 메서드와 동일하지만 지정된 값으로 구분됩니다.

$b = [MyClass]::new()
$b.DoSomething(42)

호출

메서드 호출을 참조하세요.

특성

세 가지 DscResource새 특성이 DscResourceKeyDscResourceMandatory추가되었습니다.

반환 형식

반환 형식은 계약입니다. 반환 값은 필요한 형식으로 변환됩니다. 반환 형식이 지정되지 않은 경우 반환 형식은 void입니다. 개체의 스트리밍이 없으며 개체를 의도적으로 또는 실수로 파이프라인에 쓸 수 없습니다.

변수의 어휘 범위 지정

다음에서는 이 릴리스에서 어휘 범위 지정이 작동하는 방식에 대한 예를 보여 줍니다.

$d = 42  # Script scope

function bar
{
    $d = 0  # Function scope
    [MyClass]::DoSomething()
}

class MyClass
{
    static [object] DoSomething()
    {
        return $d  # error, not found dynamically
        return $script:d # no error

        $d = $script:d
        return $d # no error, found lexically
    }
}

$v = bar
$v -eq $d # true

예: 사용자 지정 클래스 만들기

다음 예제에서는 HTML DSL(동적 스타일시트 언어)을 구현하는 몇 가지 새로운 사용자 지정 클래스를 만듭니다. 이 예제에서는 모듈 범위 밖에서 형식을 사용할 수 없으므로 도우미 함수를 추가하여 제목 스타일 및 테이블과 같은 요소 클래스의 일부로 특정 요소 형식을 만듭니다.

# Classes that define the structure of the document
#
class Html
{
    [string] $docType
    [HtmlHead] $Head
    [Element[]] $Body

    [string] Render()
    {
        $text = "<html>`n<head>`n"
        $text += $Head
        $text += "`n</head>`n<body>`n"
        $text += $Body -join "`n" # Render all of the body elements
        $text += "</body>`n</html>"
        return $text
    }
    [string] ToString() { return $this.Render() }
}

class HtmlHead
{
    $Title
    $Base
    $Link
    $Style
    $Meta
    $Script
    [string] Render() { return "<title>$Title</title>" }
    [string] ToString() { return $this.Render() }
}

class Element
{
    [string] $Tag
    [string] $Text
    [hashtable] $Attributes
    [string] Render() {
        $attributesText= ""
        if ($Attributes)
        {
            foreach ($attr in $Attributes.Keys)
            {
                $attributesText = " $attr=`"$($Attributes[$attr])`""
            }
        }

        return "<${tag}${attributesText}>$text</$tag>`n"
    }
    [string] ToString() { return $this.Render() }
}

#
# Helper functions for creating specific element types on top of the classes.
# These are required because types aren't visible outside of the module.
#
function H1 {[Element] @{Tag = "H1"; Text = $args.foreach{$_} -join " "}}
function H2 {[Element] @{Tag = "H2"; Text = $args.foreach{$_} -join " "}}
function H3 {[Element] @{Tag = "H3"; Text = $args.foreach{$_} -join " "}}
function P  {[Element] @{Tag = "P" ; Text = $args.foreach{$_} -join " "}}
function B  {[Element] @{Tag = "B" ; Text = $args.foreach{$_} -join " "}}
function I  {[Element] @{Tag = "I" ; Text = $args.foreach{$_} -join " "}}
function HREF
{
    param (
        $Name,
        $Link
    )

    return [Element] @{
        Tag = "A"
        Attributes = @{ HREF = $link }
        Text = $name
    }
}
function Table
{
    param (
        [Parameter(Mandatory)]
        [object[]]
            $Data,
        [Parameter()]
        [string[]]
            $Properties = "*",
        [Parameter()]
        [hashtable]
            $Attributes = @{ border=2; cellpadding=2; cellspacing=2 }
    )

    $bodyText = ""
    # Add the header tags
    $bodyText +=  $Properties.foreach{TH $_}
    # Add the rows
    $bodyText += foreach ($row in $Data)
                {
                            TR (-join $Properties.Foreach{ TD ($row.$_) } )
                }

    $table = [Element] @{
                Tag = "Table"
                Attributes = $Attributes
                Text = $bodyText
            }
    $table
}
function TH  {([Element] @{Tag="TH"; Text=$args.foreach{$_} -join " "})}
function TR  {([Element] @{Tag="TR"; Text=$args.foreach{$_} -join " "})}
function TD  {([Element] @{Tag="TD"; Text=$args.foreach{$_} -join " "})}

function Style
{
    return  [Element]  @{
        Tag = "style"
        Text = "$args"
    }
}

# Takes a hash table, casts it to and HTML document
# and then returns the resulting type.
#
function Html ([HTML] $doc) { return $doc }

추가 정보

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

사용자 지정 PowerShell Desired State Configuration 리소스 빌드