about_Classes_and_DSC
Breve descrizione
Viene descritto come usare le classi per sviluppare in PowerShell con Desired State Configuration (DSC).
Descrizione lunga
A partire da Windows PowerShell 5.0, il linguaggio è stato aggiunto per definire classi e altri tipi definiti dall'utente, usando la sintassi formale e la semantica simili ad altri linguaggi di programmazione orientati agli oggetti. L'obiettivo è consentire agli sviluppatori e ai professionisti IT di adottare PowerShell per una gamma più ampia di casi d'uso, semplificare lo sviluppo di artefatti di PowerShell, ad esempio le risorse DSC, e accelerare la copertura delle superfici di gestione.
Scenari supportati
Sono supportati gli scenari indicati di seguito:
- Definire le risorse DSC e i relativi tipi associati usando il linguaggio di PowerShell.
- Definire tipi personalizzati in PowerShell usando costrutti di programmazione familiari orientati agli oggetti, ad esempio classi, proprietà, metodi ed ereditarietà.
- Eseguire il debug dei tipi usando il linguaggio di PowerShell.
- Generare e gestire le eccezioni usando meccanismi formali e a livello corretto.
Definire le risorse DSC con le classi
Oltre alle modifiche della sintassi, le principali differenze tra una risorsa DSC definita dalla classe e un provider di risorse DSC cmdlet sono gli elementi seguenti:
- Non è necessario un file MOF (Management Object Format).
- Non è necessaria una sottocartella DSCResource nella cartella del modulo.
- Un file di modulo di PowerShell può contenere più classi di risorse DSC.
Creare un provider di risorse DSC definito dalla classe
L'esempio seguente è un provider di risorse DSC definito dalla classe salvato come modulo, MyDSCResource.psm1. È sempre necessario includere una proprietà chiave in un provider di risorse DSC definito dalla classe.
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
}
}
Creare un manifesto del modulo
Dopo aver creato il provider di risorse DSC basato su classe e averlo salvato come modulo, creare un manifesto del modulo per il modulo. Per rendere disponibile una risorsa basata su classi per il motore DSC, è necessario includere un'istruzione DscResourcesToExport
nel file manifesto per indicare al modulo di esportare la risorsa. In questo esempio, il manifesto del modulo seguente viene salvato come 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 = ''
}
Distribuire un provider di risorse DSC
Distribuire il nuovo provider di risorse DSC creando una cartella MyDscResource in $pshome\Modules
o $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
.
Non è necessario creare una sottocartella DSCResource. Copiare i file del modulo e del manifesto del modulo (MyDscResource.psm1 e MyDscResource.psd1) nella cartella MyDscResource.
A questo punto, creare ed eseguire uno script di configurazione come si farebbe con qualsiasi risorsa DSC.
Creare uno script di configurazione DSC
Dopo aver salvato i file di classe e manifesto nella struttura di cartelle come descritto in precedenza, è possibile creare una configurazione che usa la nuova risorsa. La configurazione seguente fa riferimento al modulo MyDSCResource. Salvare la configurazione come script MyResource.ps1.
Per informazioni su come eseguire una configurazione DSC, vedere panoramica di Windows PowerShell Desired State Configuration.
Prima di eseguire la configurazione, creare C:\test.txt
. La configurazione controlla se il file esiste in c:\test\test.txt
. Se il file non esiste, la configurazione copia il file da 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
Eseguire questo script come qualsiasi script di configurazione DSC. Per avviare la configurazione, eseguire le operazioni seguenti in una console di PowerShell con privilegi elevati:
PS C:\test> .\MyResource.ps1
Ereditarietà nelle classi di PowerShell
Dichiarare le classi di base per le classi di PowerShell
È possibile dichiarare una classe di PowerShell come tipo di base per un'altra classe di PowerShell, come illustrato nell'esempio seguente, in cui fruit è un tipo di base per apple.
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
Dichiarare le interfacce implementate per le classi di PowerShell
È possibile dichiarare interfacce implementate dopo i tipi di base o immediatamente dopo i due punti (:
) se non è specificato alcun tipo di base. Usare le virgole per separare tutti i nomi di tipo. Si tratta di una sintassi simile a C#.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Chiamare costruttori di classi di base
Per chiamare un costruttore di classe base da una sottoclasse, aggiungere la base
parola chiave , come illustrato nell'esempio seguente:
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
Se una classe base ha un costruttore predefinito (nessun parametro), è possibile omettere una chiamata esplicita al costruttore, come illustrato.
class C : B
{
C([int]$c) {}
}
Chiamare i metodi della classe base
È possibile eseguire l'override di metodi esistenti nelle sottoclassi. Per eseguire l'override, dichiarare i metodi usando lo stesso nome e la stessa firma.
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
Per chiamare i metodi della classe base dalle implementazioni sottoposte a override, eseguire il cast alla classe ([baseclass]$this)
base alla chiamata.
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
Tutti i metodi di PowerShell sono virtuali. È possibile nascondere metodi .NET non virtuali in una sottoclasse usando la stessa sintassi eseguita per un override: dichiarare metodi con lo stesso nome e firma.
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
Limitazioni correnti con l'ereditarietà della classe
Una limitazione con l'ereditarietà della classe è che non esiste alcuna sintassi per dichiarare le interfacce in PowerShell.
Definizione di tipi personalizzati in PowerShell
Windows PowerShell 5.0 ha introdotto diversi elementi del linguaggio.
Parola chiave class
Definisce una nuova classe.
La class
parola chiave è un vero tipo .NET Framework.
I membri della classe sono pubblici.
class MyClass
{
}
Parola chiave enum ed enumerazioni
È stato aggiunto il supporto per la enum
parola chiave ed è una modifica che causa un'interruzione. Il enum
delimitatore è attualmente una nuova riga. Una soluzione alternativa per coloro che già usano enum
consiste nell'inserire una e commerciale (&
) prima della parola. Limitazioni correnti: non è possibile definire un enumeratore in termini di se stesso, ma è possibile inizializzare enum
in termini di un altro enum
, come illustrato nell'esempio seguente:
Impossibile specificare il tipo di base. Il tipo di base è sempre [int].
enum Color2
{
Yellow = [Color]::Blue
}
Un valore di enumeratore deve essere una costante in fase di analisi. Il valore dell'enumeratore non può essere impostato sul risultato di un comando richiamato.
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
Enum
supporta operazioni aritmetiche, come illustrato nell'esempio seguente:
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
Parola chiave nascosta
La hidden
parola chiave, introdotta in Windows PowerShell 5.0, nasconde i membri della classe dai risultati predefinitiGet-Member
. Specificare la proprietà nascosta come illustrato nella riga seguente:
hidden [type] $classmember = <value>
I membri nascosti non vengono visualizzati utilizzando il completamento tramite tabulazione o IntelliSense, a meno che il completamento non venga eseguito nella classe che definisce il membro nascosto.
È stato aggiunto un nuovo attributo System.Management.Automation.HiddenAttribute, in modo che il codice C# possa avere la stessa semantica all'interno di PowerShell.
Per altre informazioni, vedere [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).
Import-DscResource
Import-DscResource
è ora una parola chiave effettivamente dinamica. PowerShell analizza il modulo radice del modulo specificato, alla ricerca delle classi che contengono l'attributo DscResource.
Proprietà
Un nuovo campo, ImplementingAssembly
, è stato aggiunto a ModuleInfo
. Se lo script definisce le classi o l'assembly caricato per i moduli ImplementingAssembly
binari è impostato sull'assembly dinamico creato per un modulo di script. Non viene impostato quando ModuleType = Manifest.
La reflection sul ImplementingAssembly
campo individua le risorse in un modulo. Ciò significa che è possibile individuare le risorse scritte in PowerShell o altri linguaggi gestiti.
Campi con inizializzatori.
[int] $i = 5
Static è supportato e funziona come un attributo, simile ai vincoli di tipo, in modo che possa essere specificato in qualsiasi ordine.
static [int] $count = 0
Il tipo è facoltativo.
$s = "hello"
Tutti i membri sono pubblici. Le proprietà richiedono un carattere di nuova riga o un punto e virgola. Se non viene specificato alcun tipo di oggetto, il tipo di proprietà è Object.
Costruttori e creazione di istanze
Le classi di PowerShell possono avere costruttori con lo stesso nome della classe. I costruttori possono essere sottoposti a overload. Sono supportati i costruttori statici.
Le proprietà con espressioni di inizializzazione vengono inizializzate prima di eseguire qualsiasi codice in un costruttore. Le proprietà statiche vengono inizializzate prima del corpo di un costruttore statico e le proprietà dell'istanza vengono inizializzate prima del corpo del costruttore non statico. Attualmente non esiste una sintassi per chiamare un costruttore da un altro costruttore, ad esempio la sintassi C#: ": this()")
. La soluzione consiste nel definire un metodo Init comune.
Di seguito sono riportati i modi per creare un'istanza di classi:
Creazione di un'istanza con il costruttore predefinito. Si noti che
New-Object
non è supportato in questa versione.$a = [MyClass]::new()
Chiamata di un costruttore con un parametro.
$b = [MyClass]::new(42)
Passaggio di una matrice a un costruttore con più parametri
$c = [MyClass]::new(@(42,43,44), "Hello")
Per questa versione, il nome del tipo è visibile solo in modo lessicale, ovvero non è visibile all'esterno del modulo o dello script che definisce la classe. Le funzioni possono restituire istanze di una classe definita in PowerShell e le istanze funzionano correttamente al di fuori del modulo o dello script.
Il Get-Member
parametro Statico elenca i costruttori, in modo da poter visualizzare gli overload come qualsiasi altro metodo. Le prestazioni di questa sintassi sono anche notevolmente più veloci rispetto a New-Object
.
Il metodo pseudo-statico denominato new funziona con i tipi .NET, come illustrato nell'esempio seguente. [hashtable]::new()
È ora possibile visualizzare gli overload del costruttore con Get-Member
o come illustrato in questo esempio:
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
Metodi
Un metodo della classe di PowerShell viene implementato come ScriptBlock con solo un blocco finale. Tutti i metodi sono pubblici. L'esempio seguente illustra la definizione di un metodo denominato DoSomething.
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
Chiamata al metodo
I metodi di overload sono supportati. I metodi di overload sono denominati uguali a un metodo esistente, ma differenziati in base ai valori specificati.
$b = [MyClass]::new()
$b.DoSomething(42)
Chiamata
Vedere Chiamata al metodo.
Attributi
Sono stati aggiunti tre nuovi attributi: DscResource
, DscResourceKey
e DscResourceMandatory
.
Tipi restituiti
Il tipo restituito è un contratto. Il valore restituito viene convertito nel tipo previsto. Se non viene specificato alcun tipo restituito, il tipo restituito è void. Non esiste alcun flusso di oggetti e oggetti non può essere scritto nella pipeline intenzionalmente o accidentalmente.
Ambito lessicale delle variabili
Di seguito viene riportato un esempio di come funziona l'assegnazione dell'ambito lessicale in questa versione.
$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
Esempio: Creare classi personalizzate
Nell'esempio seguente vengono create diverse classi personalizzate per implementare un linguaggio del foglio di stile dinamico HTML (DSL). Nell'esempio vengono aggiunte funzioni helper per creare tipi di elemento specifici come parte della classe di elemento, ad esempio stili di intestazione e tabelle, perché i tipi non possono essere usati all'esterno dell'ambito di un modulo.
# 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 }
Vedi anche
Compilare risorse personalizzate di PowerShell Desired State Configuration
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per