Erweiterte Funktionen in PowerShell

Inhaltsverzeichnis

Post Featured Image

Erweiterte PowerShell-Funktionen bieten die Möglichkeit, ein einfaches PowerShell-Script zu einem Tool zu erweitern, das sich wie ein natives PowerShell-Cmdlet verhält. Mit den erweiterten PowerShell-Funktionen können reguläre Scripts denselben Satz von Funktionen verwenden, der für systemeigene Cmdlets verfügbar ist.

Die erweiterten PowerShell-Funktionen basieren auf vier Säulen.

  1. Stream-Steuerung
  2. Parameter-Validierung
  3. Pipeline-Bindung
  4. Safeguards

In den folgenden Abschnitten werden diese ausführlicher behandelt.

 

Stream-Steuerung

Die PowerShell-Engine stellt eine Reihe von Streams bereit, die je nach Kontext zum Senden verschiedener Nachrichten verwendet werden können.

  1. Output / Success
  2. Error
  3. Warning
  4. Verbose
  5. Debug

In Abhängigkeit vom Kontext können eingebaute Cmdlets dazu verwendet werden, um in einen bestimmten Stream zu schreiben. Wenn wir beispielsweise in den Verbose-Stream schreiben möchten, können wir das Cmdlet „Write-Verbose“ usw. verwenden.

Es reicht jedoch nicht aus, nur das richtige Cmdlet zu verwenden, wie man in Abbildung 1 sehen kann.

Screenshot: Regular function trying to use Verbose stream

Abb. 1: Eine reguläre Funktion, die versucht, den Verbose-Stream zu verwenden

Damit der Verbose-Switch funktioniert, müssen wir unsere Funktion ändern und CmdletBinding hinzufügen, wie das folgende Beispiel zeigt:

Function Do-SomethingRegular{
		[CmdletBinding()]
		param(
				[String]$Message
		)
		Write-Verbose "We are going to write the message by the User"
		Write-Output $Message
}
Do-SomethingRegular -Message "Hi Mom" -Verbose

Das CmdletBinding-Attribut stellt eine Reihe von Funktionen bereit, die von der PowerShell-Engine bereitgestellt werden. Man kann sagen, dass das CmdletBinding-Attribut unsere Funktion erweitert.

Wie man in Abbildung 2 sehen kann, schreibt unsere Funktion korrekt in den Verbose-Stream.

Screenshot: Adding CmdletBinding attribute to the function leads to the function correctly writing to the verbose stream

Abb. 2: Das Hinzufügen des CmdletBinding-Attributs zur Funktion führt dazu, dass die Funktion korrekt in den Verbose-Stream schreibt.

Als Nächstes betrachten wir ein komplexeres Beispiel, das den Error-Stream beinhaltet.
Zunächst müssen wir den Unterschied zwischen terminierenden und nicht-terminierenden Fehlern definieren:

  • Ein terminierender Fehler ist ein fataler Fehler, der die Ausführung des Scripts stoppt
  • Ein nicht-terminierender Fehler ist weniger schwerwiegend und führt nicht zum Abbruch der Scriptausführung

Wenn die PowerShell-Engine auf einen nicht-terminierenden Fehler stößt, prüft sie zunächst den Wert der Variablen $ErrorActionPreference, um festzustellen, ob sie die Ausführung fortsetzen oder abbrechen soll. Standardmäßig ist dieser Wert auf „Continue“ gesetzt, was bedeutet, dass die PowerShell-Engine bei einem Fehler einfach mit der Verarbeitung des restlichen Scripts fortfahren soll.

Wir können dieses Verhalten in Abbildung 3 sehen: Wenn die PowerShell-Engine auf einen nicht-terminierenden Fehler stößt, erzeugt sie eine Fehlermeldung und fährt mit der Ausführung des restlichen Scripts fort.

Screenshot: Non-terminating error in PowerShell

Abb. 3: Die PowerShell-Engine fährt mit der Ausführung des Scripts fort, nachdem sie auf einen nicht-terminierenden Fehler gestoßen ist.

Aber was wäre, wenn wir das Script beenden wollten, sobald wir auf den nicht-terminierenden Fehler stoßen?
Die Lösung besteht darin, den Wert der ErrorActionPreference-Variablen auf „Stop“ zu ändern.
Die Änderung dieser Variable hat jedoch Auswirkungen auf alle anderen Scripte auf dem System, da es sich um eine globale Variable handelt. Dieses Problem lässt sich dadurch lösen, dass man den ErrorAction-Switch verwendet (siehe Abbildung 4) und die PowerShell-Engine anweist, das Script so zu behandeln, als ob die Variable ErrorActionPreference auf „Stop“ gesetzt wäre.

creenshot: Terminating error in PowerShell

Nochmals, der Grund, warum uns der ErrorAction-Switch zur Verfügung steht, ist, dass wir erweiterte Funktionen verwenden.

 

Parameter-Validierung

Funktionen sind nicht besonders nützlich, wenn sie keine Parameter akzeptieren können. Hilfsfunktionen sind eine andere Geschichte, obwohl unser Schwerpunkt auf regulären Funktionen liegt.
Daher ist die Parameter-Validierung ein sehr wichtiger Schritt, bevor wir beginnen, die gelieferten Parameter in unserer Programmierlogik zu verwenden.

Die PowerShell-Engine bietet eine Reihe von integrierten Mechanismen zur Validierung von Parametern. Erweiterte Funktionen können uns helfen, diese Funktionalität zur Validierung unserer Parameter zu nutzen, anstatt unsere eigene Parameter-Validierungslogik zu schreiben.
In diesem Abschnitt werden wir einige Parameter-Validierungstechniken besprechen. Diese Liste ist jedoch keineswegs erschöpfend.

1. Mandatory Parameter

Mandatory Parameter helfen dabei, bestimmte Parameter so zu deklarieren, wie sie von der Funktion benötigt werden. Wenn der Wert fehlt, fordert die PowerShell-Engine den Benutzer auf, einen Wert einzugeben (wie in Abbildung 5 dargestellt).
Hier ein Beispiel:

# Writing a function with a mandatory parameter
Function Do-SomethingRegular {
		[CmdletBinding]()]
		Param(
				[Parameter(Mandatory)]
				[String] $Message
		)
		Write-Output $Message
}

# Calling the function
Do-SomethingRegular
Screenshot: Declaring a PowerShell parameter as mandatory

Fig. 5: Declaring a parameter as mandatory

2. ValidateScript

ValidateScript kann verwendet werden, um ein Script gegen den angegebenen Parameterwert auszuführen. Wenn das Script $true zurückgibt, dann wird der Parameterwert akzeptiert.
Hier ist ein Beispiel:

Function Simple-Function {
		[CmdletBinding()]
		param(
				[Parameter(Mandatory)]
				[ValidateScript({Test-Connection -ComputerName $_ -Quiet -Count 1})]
				[string]$computerName
		)
		$ErrorActionPreference
		Get-CimInstance -ClassName win32_Process -ComputerName $ComputerName
}

Simple-Function -ComputerName 'CAT'

Wie wir in Abbildung 6 sehen können, verwenden wir ValidateScript, um zu testen, ob wir die vom Benutzer als ComputerName-Parameter angegebene Maschine erreichen können. Das Script verwendet das Cmdlet Test-Connection mit dem Flag „Quiet“, um entweder „true“ oder „false“ zurückzugeben.
Da unser Labor über keinen Computer mit dem Namen „CAT“ verfügt, gibt es „false“ zurück, und ValidateScript akzeptiert den Parameterwert nicht.

Screenshot: Validating a PowerShell script with the ValidateScript parameter

Fig. 6: Validating a PowerShell script with the ValidateScript parameter

3. ValidatePattern

ValidatePattern kann verwendet werden, um einen Parameterwert gegen einen regulären Ausdruck zu validieren. Hier ein Beispiel:

Function Do-SomethingRegular {
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidatePattern('\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(.|$)){4}\b')]
[String] $MachineName
)
Get-CimInstance -ComputerName $MachineName -ClassName win32_Process
}
Do-SomethingRegular -MachineName 127.0.01.1
Wie in Abbildung 7 gezeigt, validieren wir den gelieferten Wert des MachineName-Parameters gegen einen regulären Ausdruck für die IP-Adresse.

Screenshot: Validating the supplied value of a prameter against a regular expression

Abb. 7: Validierung des gelieferten Wertes eines Parameters gegen einen regulären Ausdruck

4. ValidateSet

ValidateSet kann verwendet werden, um akzeptable Werte für einen Parameter vorzudefinieren (wie in Abbildung 8 gezeigt). Wenn der Benutzer andere als die vordefinierten Werte liefert, gibt die Funktion einen Fehler zurück. Hier ist ein Beispiel:

Function Do-SomethingRegular{
		[CmdletBinding()]
		Param(
				[Parameter(Mandatory)]
				[ValidateSet('Mom','Dad')]
				[String] $Parent
		)
		Write-Output "Hello " $Parent
}

Do-SomethingRegular -Parent
Screenshot: Predefining acceptable values for a parameter with ValidateSet

Abb. 8: Vordefinieren akzeptabler Werte für einen Parameter mit ValidateSet

Wie bereits erwähnt, bietet PowerShell eine Reihe von Parameter-Validierungstechniken, dieser Blog-Post hat jedoch nur einige davon behandelt. Weitere Einzelheiten finden Sie im Artikel von Bruno Buyck über Parameter-Validierungskonzepte in PowerShell und ScriptRunner, eine vollständige Liste finden Sie in der offiziellen Microsoft PowerShell-Dokumentation.

 

Pipeline-Bindung

Parameter-Pipeline-Bindung ist eines der wichtigsten Konzepte in PowerShell. In diesem Abschnitt wird nur darauf eingegangen, wie Parameter für die Annahme von Pipeline-Eingaben aktiviert werden können. Weitere Einzelheiten finden Sie in meinem Artikel über Parameter-Bindungskonzepte in PowerShell.
Die PowerShell-Engine bindet die Pipeline-Eingabe nach Wert – „by Value“ – oder nach Eigenschaft – „by Property“. Mit erweiterten Funktionen können wir angeben, welche Parameter Pipeline-Eingaben akzeptieren können und wie die Bindung erfolgen soll.

Nehmen wir ein einfaches Beispiel, bei dem wir die Pipeline-Eingabe byValue akzeptieren:

Function Do-SomethingRegular{
		[CmdletBinding()]
		Param(
				[Parameter(Mandatory,valueFromPipeline)]
				[String] $name
		)
		Write-Output "You Entered $name"
}
"Sonny"|Do-SomethingRegular

Abbildung 9 zeigt, dass ein Zeichenkettenobjekt an unsere Funktion übergeben wird, und da die Funktion über einen Parameter verfügt, der den Wert aus der Pipeline akzeptiert, wird die Funktion ohne Fehler ausgeführt.

Screenshot: Passing a string object as parameter value to a function through the pipeline

Abb. 9: Übergeben eines Zeichenkettenobjekts als Parameterwert an eine Funktion über die Pipeline

In ähnlicher Weise können wir unsere Funktion so ändern, dass sie die Pipeline-Eingabe byProperty akzeptiert:

Function Do-SomethingRegular{
		[CmdletBinding()]
		Param(
				[Parameter(Mandatory,valueFromPipelineByProperty)]
				[String] $name
		)
		Write-Output "You Entered $name"
}
$MyNewObject = [PSCustomObject]@{name = 'Sonny'; age = 21}
$MyNewObject | Do-SomethingRegular

Wie in Abbildung 10 dargestellt, haben wir ein Objekt mit einem der Eigenschaft name erstellt, der dem Namen unseres Parameters entspricht.
Wenn dieses Objekt in der Pipeline übergeben wird, bindet die PowerShell-Engine die Eigenschaft mit den Parametern, da diese einen übereinstimmenden Namen haben.

Screenshot: Passing a single property of an object as parameter value to a function through the pipeline

Abb. 10: Übergeben einer einzelnen Eigenschaft eines Objekts als Parameterwert an eine Funktion über die Pipeline

Machen wir unser Beispiel noch ein bisschen kniffliger, indem wir mehr als ein Objekt in der Pipeline übergeben. Da wir es mit mehreren Objekten zu tun haben, müssen wir Methoden zur Eingabeverarbeitung verwenden. Methoden zur Eingabeverarbeitung sind auch als BEGIN-, PROCESS-
und END-Block bekannt. Ein guter PowerShell-Programmierer wird sie immer verwenden, wenn er eine erweiterte Funktion schreibt.

Wie wir in Abbildung 11 sehen können, folgt die Bindung selbst bei mehreren Objekten demselben Prinzip wie in unserem vorherigen Beispiel:

Function Do-SomethingRegular{
		[CmdletBinding()]
		Param(
				[Parameter(Mandatory,valueFromPipelineByPropertyName)]
				[String] $name
		)
		process{
		Write-Output "You Entered $name"
		}
}

$MyNewObject1 = [PSCustomObject]@{name = 'Sonny';age = 21}
$MyNewObject2 = [PSCustomObject]@{name = 'browninfosecguy';age = 21}
$MyNewObject1,$MyNewObject2|Do-SomethingRegular
Screenshot: Passing a property of multiple objects as parameter values to a function through the pipeline

Abb. 11: Übergeben einer Eigenschaft mehrerer Objekte als Parameterwerte an eine Funktion über die Pipeline 

 

Safeguards

PowerShell bietet zwei eingebaute Schutzmechanismen/Safeguards.

  • WhatIf
  • Confirm

Diese Schutzmechanismen werden durch zwei Systemvariablen gesteuert $WhatIfPreference und $ConfirmPreference. Der Standardwert dieser beiden Variablen ist in Abbildung 12 dargestellt.

Screenshot: The default value for WhatIf and Confirm Preference variable

Abb. 12: Der Standardwert für die Variablen WhatIf und Confirm Preference 


WhatIf

Der WhatIf-Switch kann verwendet werden, um zu sehen, „was passiert wäre“, ohne wirklich etwas zu verändern. Sein Verhalten wird durch die $WhatIfPreference-Variable gesteuert.
Der Standardwert der $WhatIfPreference-Variable ist auf „false“ gesetzt, was der Grund dafür ist, dass uns beim Ausführen eines beliebigen Cmdlets kein „WhatIf“-Szenario angezeigt wird.
Um das WhatIf-Szenario anzuzeigen, müssen wir explizit den WhatIf-Switch verwenden, wie in Abbildung 13 dargestellt.

Screenshot: WhatIf functionality demonstrated in PowerShell

Abb. 13: Beispiel für den -WhatIf-Switch in PowerShell.

Die WhatIf-Funktionalität in einer Funktion kann folgendermaßen implementiert werden:

  • Hinzufügen des SupportsShouldProcess-Arguments zu CmdletBinding, wie in Abbildung 14 gezeigt
  • Erfassen des von $PSCmdlet.ShouldProcess(“”) zurückgegebenen booleschen Werts; wenn TRUE bedeutet dies, dass der Benutzer die WhatIf-Option nicht ausgewählt hat
  • Platzieren der WhatIf-Logik in einer If-Schleife um $PSCmdlet.ShouldProcess(„“)

Das folgende Beispiel veranschaulicht diese Schritte:

Function Delete-Windows{
# First we're adding the “SupportsShouldProcess” argument to CmdletBinding
		[CmdletBinding(SupportsShouldProcess)]
		param(
		)
# Then, we're capturing the Boolean value returned by “$PSCmdlet.ShouldProcess("")”, If TRUE it means user has not selected whatif option
		if($PSCmdlet.ShouldProcess("Meow")){
				Write-Host "WhatIf flag not set, delete windows"
		}
		else{
				Write-Host "WhatIf flag set, do not delete windows or real"
		}
}
Screenshot: Implementing WhatIf functionality in a function

Abb. 14: Implementieren der WhatIf-Funktionalität in einer Funktion

 


Confirm

Der Confirm-Switch kann verwendet werden, um einen Benutzer vor dem Ausführen des Cmdlets zur Bestätigung aufzufordern. Wir können dasselbe in unserer Funktion implementieren. Wie bereits erwähnt, wenn die Variable $ConfirmPreference auf „High“ festgelegt ist, fordern die Cmdlets, bei denen die Variable ConfirmImpact auf „High“ festgelegt ist, den Benutzer vor der Ausführung des Cmdlets zur Bestätigung auf.
Dies bringt uns zu einem weiteren Konzept, der $ConfirmImpact-Variablen, die zur Definition des Schweregrads einer Funktion verwendet wird. Sie kann die folgenden Werte annehmen:

  • „Low“
  • „Medium“
  • „High“

Wir können sie auch auf „None“ setzen, aber dann wird sie einfach deaktiviert.
Um confirm in unserer Funktion zu implementieren, müssen wir das ConfirmImpact-Argument zu CmdletBinding hinzufügen und einen Wert dafür initiieren. Das PowerShell-Modul vergleicht dann diesen Wert mit dem Wert von $ConfirmPreference, und wenn der Schweregrad unserer Funktion gleich oder höher ist, wird der Benutzer mit einem Confirm-Dialogfeld dazuaufgefordert, die Ausführung zu bestätigen. Hier ein Beispiel:

Function Delete-Windows{
		[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")]
		param(
		)
		if($PSCmdlet.ShouldProcess("Meow"){
		Write-Host "Delete Windows"
		}		
}

Wie wir in Abbildung 15 sehen können, hat das PowerShell-Modul den Benutzer nicht mit einem Dialog zur Bestätigung der Ausführung aufgefordert, da der Wert für $ConfirmPreference auf „High“ gesetzt ist und unsere Funktion einen „Medium“ ConfirmImpact hat.

Screenshot: Adding the ConfirmImpact argument to CmdletBinding and setting its value to

Abb. 15: Hinzufügen des ConfirmImpact-Arguments zu CmdletBinding und Setzen des Werts auf „Medium“

Wenn wir den Wert von $ConfirmPreference auf „Medium“ ändern, wird der Benutzer wie in Abbildung 16 gezeigt per Dialogfeld zur Bestätigung der Ausführung aufgefordert.

 
Screenshot: Confirm dialogue box, prompting the user whether they want to perform the operation 'Delete-Windows' on target 'Meow'

Abb. 16: Wenn der Wert von $ConfirmPreference auf „Medium“ geändert wird, wird der Benutzer im Dialogfeld aufgefordert, die Ausführung der Funktion zu bestätigen.

Was geschieht nun, wenn wir die Variable $ConfirmPreference wieder auf „High“ zurücksetzen? Wie erwartet, werden wir, wenn wir nur „Delete-Windows“ ausführen, nicht per Dialogfeld zur Bestätigung aufgefordert; wenn wir jedoch explizit den Schalter „Confirm“ verwenden, wird uns das Dialogfeld angezeigt, wie in Abbildung 17 dargestellt.

 
Screenshot: Adding the -Confirm switch when calling the function triggers the confirm dialogue box

Abb. 17: Das Hinzufügen des Schalters -Confirm beim Aufruf der Funktion löst das Dialogfeld aus 


Fazit

Der Wert erweiterter PowerShell-Funktionen ist klar ersichtlich. Sie reduzieren die unnötige Last der Implementierungslogik für die Parameter-Validierung, die Implementierung von Sicherheitsvorkehrungen und andere in diesem Blogbeitrag diskutierte Funktionen.

Wie immer, wenn Sie Fragen oder Bedenken haben, zögern Sie bitte nicht, uns zu kontaktieren.

Zusammenhängende Posts

16 min read

ScriptRunner Portal Edition R4

4 min read

So nutzen Sie Azure Templates zur Automatisierung

Über den Autor: