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 리소스 공급자 배포
$pshome\Modules
또는 $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
MyDscResource 폴더를 만들어 새 DSC 리소스 공급자를 배포합니다.
DSCResource 하위 폴더를 만들 필요가 없습니다. 모듈 및 모듈 매니페스트 파일(MyDscResource.psm1 및 MyDscResource.psd1)을 MyDscResource 폴더에 복사합니다.
이 시점에서 모든 DSC 리소스와 마찬가지로 구성 스크립트를 만들고 실행합니다.
DSC 구성 스크립트 만들기
앞에서 설명한 대로 폴더 구조에 클래스 및 매니페스트 파일을 저장한 후 새 리소스를 사용하는 구성을 만들 수 있습니다. 다음 구성은 MyDSCResource 모듈을 참조합니다. 구성을 스크립트로 저장하고 MyResource.ps1.
DSC 구성을 실행하는 방법에 대한 자세한 내용은 Windows PowerShell 필요한 상태 구성 개요참조하세요.
구성을 실행하기 전에 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 클래스의 기본 형식으로 선언할 수 있습니다. 이 예제에서는 과일apple기본 형식입니다.
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
키워드는 진정한 .NET Framework 형식입니다.
클래스 멤버는 공용입니다.
class MyClass
{
}
열거형 키워드 및 열거형
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
Static 매개 변수는 생성자를 나열하므로 다른 메서드와 같은 오버로드를 볼 수 있습니다. 이 구문의 성능도 New-Object
훨씬 빠릅니다.
새 이름이 [hashtable]::new()
이제 Get-Member
사용하거나 이 예제와 같이 생성자 오버로드를 볼 수 있습니다.
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
방법
PowerShell 클래스 메서드는 엔드 블록만 있는 ScriptBlock 구현됩니다. 모든 메서드는 public입니다. 다음은 doSomething
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
메서드 호출
오버로드된 메서드가 지원됩니다. 오버로드된 메서드의 이름은 기존 메서드와 동일하지만 지정된 값으로 구분됩니다.
$b = [MyClass]::new()
$b.DoSomething(42)
호출
메서드 호출참조하세요.
특성
DscResource
, DscResourceKey
및 DscResourceMandatory
세 가지 새 특성이 추가되었습니다.
반환 형식
반환 형식은 계약입니다. 반환 값이 예상 형식으로 변환됩니다. 반환 형식이 지정되지 않은 경우 반환 형식은 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 }
참고 항목
사용자 지정 PowerShell 필요한 상태 구성 리소스 빌드