Freigeben über


about_Classes_and_DSC

Kurze Beschreibung

Beschreibt, wie Sie Klassen zum Entwickeln in PowerShell mit Desired State Configuration (DSC) verwenden können.

Lange Beschreibung

Ab Windows PowerShell 5.0 wurde die Sprache hinzugefügt, um Klassen und andere benutzerdefinierte Typen zu definieren, indem formale Syntax und Semantik verwendet werden, die anderen objektorientierten Programmiersprachen ähnlich sind. Ziel ist es Entwicklern und IT-Experten, PowerShell für eine breitere Palette von Anwendungsfällen zu nutzen, die Entwicklung von PowerShell-Artefakten wie DSC-Ressourcen zu vereinfachen und die Abdeckung von Verwaltungsoberflächen zu beschleunigen.

Unterstützte Szenarios

Die folgenden Szenarien werden unterstützt:

  • Definieren Sie DSC-Ressourcen und ihre zugeordneten Typen mithilfe der PowerShell-Sprache.
  • Definieren Sie benutzerdefinierte Typen in PowerShell mithilfe vertrauter objektorientierter Programmierkonstrukte, z. B. Klassen, Eigenschaften, Methoden und Vererbung.
  • Debuggen von Typen mithilfe der PowerShell-Sprache.
  • Generieren und Behandeln von Ausnahmen mithilfe formaler Mechanismen und auf der richtigen Ebene.

Definieren von DSC-Ressourcen mit Klassen

Neben Syntaxänderungen sind die wichtigsten Unterschiede zwischen einer klasse definierten DSC-Ressource und einem Cmdlet DSC-Ressourcenanbieter die folgenden Elemente:

  • Eine MOF-Datei (Management Object Format) ist nicht erforderlich.
  • Der Unterordner DSCResource im Ordner „module“ ist nicht erforderlich.
  • Eine PowerShell-Moduldatei kann mehrere DSC-Ressourcenklassen enthalten.

Erstellen eines klassenspezifischen DSC-Ressourcenanbieters

Im folgenden Beispiel handelt es sich um einen klassendefinierten DSC-Ressourcenanbieter, der als Modul gespeichert wird, MyDSCResource.psm1. Sie müssen immer eine Schlüsseleigenschaft in einen klassenspezifischen DSC-Ressourcenanbieter einschließen.

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
    }
}

Erstellen eines Modulmanifests

Nach dem Erstellen des klassenbasierten DSC-Ressourcenanbieters und dessen Speicherung als Modul erstellen Sie ein Modulmanifest für das Modul. Um eine klassenbasierte Ressource für die DSC-Engine verfügbar zu machen, müssen Sie eine DscResourcesToExport-Anweisung zur Manifestdatei hinzufügen, die die Engine anweist, die Ressource zu exportieren. Bei diesem Beispiel wird das folgende Modulmanifest als MyDscResource.psd1 gespeichert.

@{

# 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 = ''

}

Bereitstellen eines DSC-Ressourcenanbieters

Stellen Sie den neuen DSC-Ressourcenanbieter bereit, indem Sie einen MyDscResource-Ordner in $pshome\Modules oder $env:SystemDrive\ProgramFiles\WindowsPowerShell\Moduleserstellen.

Sie müssen nicht den Unterordner „DSCResource“ erstellen. Kopieren Sie die Modul- und Modulmanifestdatei (MyDscResource.psm1 und MyDscResource.psd1) in den Ordner MyDscResource.

Anschließend muss wie bei allen anderen DSC-Ressourcen ein Konfigurationsskript erstellt und ausgeführt werden.

Erstellen eines DSC-Konfigurationsskripts

Nachdem Sie die Klasse und die Manifestdateien, wie zuvor beschrieben, in der Ordnerstruktur gespeichert haben, können Sie eine Konfiguration erstellen, die die neue Ressource verwendet. Die folgende Konfiguration verweist auf das MyDSCResource-Modul. Speichern Sie die Konfiguration als Skript, MyResource.ps1.

Informationen zum Ausführen einer DSC-Konfiguration finden Sie unter Windows PowerShell Desired State Configuration Übersicht.

Bevor Sie die Konfiguration ausführen, erstellen C:\test.txtSie . Die Konfiguration überprüft, ob die Datei vorhanden ist.c:\test\test.txt Wenn die Datei nicht vorhanden ist, kopiert die Konfiguration die Datei aus 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

Führen Sie dieses Skript aus, da Sie ein DSC-Konfigurationsskript ausführen würden. Führen Sie folgendes aus, um die Konfiguration in einer PowerShell-Konsole mit erhöhten Rechten zu starten:

PS C:\test> .\MyResource.ps1

Vererbung in PowerShell-Klassen

Deklarieren von Basisklassen für PowerShell-Klassen

Sie können eine PowerShell-Klasse als Basistyp für eine andere PowerShell-Klasse deklarieren, wie im folgenden Beispiel gezeigt, in dem Obst ein Basistyp für Apple ist.

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

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

Deklarieren implementierter Schnittstellen für PowerShell-Klassen

Sie können implementierte Schnittstellen nach Basistypen oder unmittelbar nach einem Doppelpunkt: () deklarieren, wenn kein Basistyp angegeben ist. Trennen Sie alle Typnamen durch Kommas. Dies ähnelt der C#-Syntax.

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

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

Aufruf von Basisklassenkonstruktoren

Wenn Sie einen Basisklassenkonstruktor aus einer Unterklasse aufrufen möchten, fügen Sie das base Schlüsselwort hinzu, wie im folgenden Beispiel gezeigt:

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

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

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

Wenn eine Basisklasse einen Standardkonstruktor (keine Parameter) aufweist, können Sie einen expliziten Konstruktoraufruf auslassen, wie gezeigt.

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

Aufrufen von Basisklassenmethoden

Sie können vorhandene Methoden in Unterklassen überschreiben. Um die Außerkraftsetzung zu erledigen, deklarieren Sie Methoden mithilfe desselben Namens und der gleichen Signatur.

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

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

Um Basisklassenmethoden aus überschriebenen Implementierungen aufzurufen, werden sie in die Basisklasse ([baseclass]$this) für Aufrufe eingefügt.

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

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

Alle PowerShell-Methoden sind virtuell. Sie können nicht virtuelle .NET-Methoden in einer Unterklasse ausblenden, indem Sie dieselbe Syntax verwenden, wie Sie für eine Außerkraftsetzung vorgehen: deklarieren Sie Methoden mit demselben Namen und derselben Signatur.

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

Aktuelle Einschränkungen mit Klassenvererbung

Eine Einschränkung der Klassenvererbung besteht darin, dass keine Syntax zum Deklarieren von Schnittstellen in PowerShell vorhanden ist.

Definieren benutzerdefinierter Typen in PowerShell

Windows PowerShell 5.0 hat mehrere Sprachelemente eingeführt.

Schlüsselwort „Class“

Definiert eine neue Klasse. Das class Schlüsselwort ist ein wahrer .NET Framework Typ. Kursmitglieder sind öffentlich.

class MyClass
{
}

Schlüsselwort „enum“ und Enumerationen

Unterstützung für das enum Schlüsselwort wurde hinzugefügt und ist eine unterbrechungsende Änderung. Das enum Trennzeichen ist derzeit eine neue Linie. Eine Problemumgehung für diejenigen, die bereits verwenden enum , besteht darin, ein Ampersand (&) vor dem Wort einzufügen. Aktuelle Einschränkungen: Sie können eine Aufzählung nicht in Bezug auf sich selbst definieren, aber Sie können in Bezug auf eine andere enuminitialisierenenum, wie im folgenden Beispiel gezeigt:

Der Basistyp kann derzeit nicht angegeben werden. Der Basistyp ist immer [int].

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

Ein Enumeratorwert muss eine Konstante zur Analysezeit sein. Der Aufzählungswert kann nicht auf das Ergebnis eines aufgerufenen Befehls festgelegt werden.

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

Enum unterstützt arithmetische Vorgänge, wie im folgenden Beispiel gezeigt:

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

Ausgeblendetes Schlüsselwort

Das hidden Schlüsselwort, das in Windows PowerShell 5.0 eingeführt wurde, blendet Klassenmitglieder aus Standardergebnissen Get-Member aus. Geben Sie die ausgeblendete Eigenschaft wie in der folgenden Zeile dargestellt an:

hidden [type] $classmember = <value>

Ausgeblendete Elemente werden nicht mithilfe von Registerkartenabschluss oder IntelliSense angezeigt, es sei denn, die Fertigstellung tritt in der Klasse auf, die das ausgeblendete Element definiert.

Ein neues Attribut, System.Management.Automation.HiddenAttribute, wurde hinzugefügt, sodass C#-Code dieselbe Semantik in PowerShell aufweisen kann.

Weitere Informationen finden Sie unter [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).

Import-DscResource

Import-DscResource ist jetzt ein tatsächlich dynamisches Schlüsselwort. PowerShell analysiert das Stammmodul des angegebenen Moduls und sucht Klassen, die das DscResource-Attribut enthalten.

Eigenschaften

Ein neues Feld, ImplementingAssemblywurde hinzugefügt ModuleInfo. Wenn das Skript Klassen definiert oder die geladene Assembly für binärmodule ImplementingAssembly auf die dynamische Assembly festgelegt wird, die für ein Skriptmodul erstellt wurde. Falls „ModuleType = Manifest“, wird es nicht festgelegt.

Die Reflexion über das ImplementingAssembly Feld entdeckt Ressourcen in einem Modul. Dies bedeutet, dass Sie Ressourcen ermitteln können, die in PowerShell oder anderen verwalteten Sprachen geschrieben wurden.

Felder mit Initializern.

[int] $i = 5

Statisch wird unterstützt und funktioniert wie ein Attribut, ähnlich wie die Typeinschränkungen, sodass sie in beliebiger Reihenfolge angegeben werden kann.

static [int] $count = 0

Ein Typ ist optional.

$s = "hello"

Alle Member sind öffentlich. Eigenschaften erfordern ein Zeilenumbruchzeichen oder Semikolon. Wenn kein Objekttyp angegeben wird, ist der Eigenschaftstyp "Object".

Konstruktoren und Instanziierung

PowerShell-Klassen können Konstruktoren haben, die denselben Namen wie ihre Klasse haben. Konstruktoren können überladen werden. Statische Konstruktoren werden unterstützt. Eigenschaften mit Initialisierungsausdrücken werden vor dem Ausführen von Code in einem Konstruktor initialisiert. Statische Eigenschaften werden vor dem Hauptteil eines statischen Konstruktors initialisiert. Instanzeigenschaften werden vor dem Hauptteil des nicht statischen Konstruktors initialisiert. Derzeit gibt es keine Syntax zum Aufrufen eines Konstruktors aus einem anderen Konstruktor wie der C#-Syntax: ": this()"). Eine Behelfslösung ist das Definieren einer allgemeinen „Init“-Methode.

Nachfolgend finden Sie Möglichkeiten zum Instanziieren von Klassen:

  • Instanziieren mithilfe des Standardkonstruktors. Beachten Sie, dass New-Object in dieser Version nicht unterstützt wird.

    $a = [MyClass]::new()

  • Aufrufen eines Konstruktors mit einem Parameter.

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

  • Übergeben ein Arrays an einen Konstruktor mit mehreren Parametern

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

Für diese Version ist der Typname nur lexikalisch sichtbar, d. h. er ist nicht außerhalb des Moduls oder Skripts sichtbar, das die Klasse definiert. Funktionen können Instanzen einer klasse zurückgeben, die in PowerShell definiert ist, und Instanzen funktionieren außerhalb des Moduls oder Skripts gut.

Der Get-Memberstatische Parameter listet Konstruktoren auf, sodass Sie Überladungen wie jede andere Methode anzeigen können. Die Leistung dieser Syntax ist auch erheblich schneller als New-Object.

Die pseudostatische Methode new funktioniert mit .NET-Typen, wie im folgenden Beispiel gezeigt. [hashtable]::new()

Sie können jetzt Konstruktorüberladungen mit Get-Member oder wie in diesem Beispiel anzeigen:

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

Methoden

Eine PowerShell-Klassenmethode wird als Skriptblock mit nur einem „end“-Block implementiert. Alle Methoden sind öffentlich. Nachstehend sehen Sie ein Beispiel der Definition einer Methode namens DoSomething.

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

Methodenaufruf

Überladene Methoden werden unterstützt. Überladene Methoden werden mit einer vorhandenen Methode benannt, unterscheiden sich jedoch nach ihren angegebenen Werten.

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

Aufruf

Siehe Methodenaufruf.

Attributes

Drei neue Attribute wurden hinzugefügt: DscResource, DscResourceKey, und DscResourceMandatory.

Rückgabetypen

Der Rückgabetyp ist ein Vertrag. Der Rückgabewert wird in den erwarteten Typ konvertiert. Falls kein Rückgabetyp angegeben wird, ist der Rückgabetyp „void“. Es gibt keine Übertragung von Objekten und Objekten, die entweder absichtlich oder versehentlich in die Pipeline geschrieben werden können.

Lexikalische Eingrenzung von Variablen

Das folgende Beispiel zeigt, wie die lexikalische Eingrenzung in dieser Version funktioniert.

$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

Beispiel: Erstellen benutzerdefinierter Klassen

Im folgenden Beispiel werden mehrere neue, benutzerdefinierte Klassen erstellt, um eine HTML Dynamic Stylesheet Language (DSL) zu implementieren. Im Beispiel werden Hilfsfunktionen hinzugefügt, um bestimmte Elementtypen als Teil der Elementklasse zu erstellen, z. B. Überschriftenformatvorlagen und Tabellen, da Typen außerhalb des Bereichs eines Moduls nicht verwendet werden können.

# 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 }

Weitere Informationen

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

Erstellen von benutzerdefinierten PowerShell-Desired State Configuration-Ressourcen