共用方式為


about_Classes_and_DSC

簡短描述

描述如何使用類別搭配 Desired State Configuration (DSC) 在 PowerShell 中開發。

完整描述

從 Windows PowerShell 5.0 開始,已新增語言來定義類別和其他使用者定義型別,方法是使用類似于其他物件導向程式設計語言的正式語法和語意。 目標是讓開發人員和 IT 專業人員能夠採用 PowerShell 以取得更廣泛的使用案例、簡化開發 PowerShell 成品,例如 DSC 資源,以及加速管理介面的涵蓋範圍。

支援的案例

以下是支援的案例:

  • 使用 PowerShell 語言定義 DSC 資源及其相關聯的類型。
  • 使用熟悉的物件導向程式設計建構,例如類別、屬性、方法和繼承,在 PowerShell 中定義自訂類型。
  • 使用 PowerShell 語言偵錯類型。
  • 使用正式機制,並在正確的層級產生和處理例外狀況。

使用類別定義 DSC 資源

除了語法變更之外,類別定義的 DSC 資源與 Cmdlet DSC 資源提供者之間的主要差異如下:

  • 不需要管理物件格式 (MOF) 檔案。
  • 不需要模組資料夾中的 DSCResource 子資料夾。
  • PowerShell 模組檔案可以包含多個 DSC 資源類別。

建立類別定義的 DSC 資源提供者

下列範例是類別定義的 DSC 資源提供者,儲存為模組 MyDSCResource.psm1。 您必須一律在類別定義的 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$pshome\Modules 建立 MyDscResource 資料夾,以部署新的 DSC 資源提供者。

您不需要建立 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 類別的基底類型,如下列範例所示, 其中水果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 關鍵字

定義新的類別。 關鍵字 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 }

Hidden 關鍵字

hiddenWindows PowerShell 5.0 中引進的 關鍵字會隱藏預設 Get-Member 結果中的類別成員。 指定隱藏屬性,如下行所示:

hidden [type] $classmember = <value>

除非在定義隱藏成員的類別中發生完成,否則隱藏成員不會使用 Tab 鍵自動完成或 IntelliSense 來顯示。

已新增 System.Management.Automation.HiddenAttribute的新屬性,讓 C# 程式碼可以在 PowerShell 中具有相同的語意。

如需詳細資訊,請參閱 [about_Hidden[ (/powershell/module/microsoft.powershell.core/about/about_hidden) 。

Import-DscResource

Import-DscResource 現在是真正的動態關鍵字。 PowerShell 會剖析所指定模組的根模組,其會搜尋包含 DscResource 屬性的類別。

屬性

已將新的欄位 ImplementingAssembly 新增至 ModuleInfo 。 如果腳本定義類別,或二進位模組 ImplementingAssembly 的載入元件會設定為針對腳本模組建立的動態元件。 當 ModuleType = Manifest 時不設定。

欄位上的 ImplementingAssembly 反映會探索模組中的資源。 這表示您可以探索以 PowerShell 或其他 Managed 語言撰寫的資源。

具有初始化運算式的欄位。

[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-MemberStatic參數會列出建構函式,因此您可以像任何其他方法一樣檢視多載。 這個語法的效能也比 New-Object 快得多。

名為 new 的 Pseudo-static 方法可搭配 .NET 類型,如下例所示。 [hashtable]::new()

您現在可以使用 Get-Member 來查看建構函式多載,或如下列範例所示:

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

方法

PowerShell 類別方法會實作為只有 End 區塊的 ScriptBlock。 所有方法都是公用的。 下例說明定義名為 DoSomething 的方法。

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

方法引動過程

支援多載方法。 多載方法的名稱與現有方法相同,但會依其指定的值來區分。

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

引動過程

請參閱 方法調用

屬性

已新增三個新屬性: DscResourceDscResourceKeyDscResourceMandatory

傳回類型

傳回類型是一個合約。 傳回值會轉換成預期的類型。 如未指定任何傳回類型,傳回類型為 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資源