PowerShell Cmdlet erstellen
PowerShell Cmdlet erstellen

Überblick

Alte Scripte und wiederkehrende Aufgaben sollen durch den neuen PowerShell-Ansatz ersetzt werden. D.h. es werden keine "langen" Skripte mehr entwickelt, sondern den Inhalt dieser Skripte auf viel kleine Cmdlets aufgeteilt. Das hat folgende Vorteile:

  • Zum Beispiel wird ein Anmelde-Skript durch Aufruf kleinerer Cmdlets übersichtlicher = Lesbarkeit.
  • Durch Parametrisierung und die Kombinationsmöglichkeiten von Cmdlets über die Pipeline | können unterschiedliche Anmelde-Skripte erzeugt werden = Wiederverwendbarkeit.
  • Mittels Show-Command oder Out-GridView können Cmdlets über eine GUI bedient werden = GUI-Komfort.
  • Mittels des PowerShell-Moduls Pester können Cmdlets automatisch getestet werden = Komponententest (UTest).

TIPP - Zu diesem Artikel gibt es auch ein passendes HowTo-Video auf meinem YouTube Kanal PowerShell Germany - Cmdlet erstellen

Um ein Cmdlet erstellen zu können ist das Knowhow der folgenden PowerShell-About-Seiten hilfreich:

# Beschreibung der eigenen Cmdlet-Hilfe für das PowerShell-Hilfesystem.
Get-Help -Name 'about_Comment_Based_Help' -ShowWindow

# Grundlagen zur Funktion/Cmdlet Erstellung.
Get-Help -Name 'about_Functions' -ShowWindow

# Vertiefende Informationen zur Cmdlet Erstellung.
Get-Help -Name 'about_Functions_Advanced' -ShowWindow

# Beschreibt die Verwendung des CmdletBinding Attributes.
Get-Help -Name 'about_Functions_Advanced_Methods' -ShowWindow

# Beschreibung der eigenen Cmdlet-Parameter u.a. deren Validierung.
Get-Help -Name 'about_Functions_Advanced_Parameters' -ShowWindow

# Beschreibung der eigenen Cmdlet-Parameter bzgl. der Pipeline-Verarbeitung.
Get-Help -Name 'about_Functions_CmdletBindingAttribute' -ShowWindow

# Beschreibung von Rückgabe-Objekte wie PSCustomObject.
Get-Help -Name 'about_Functions_OutputTypeAttribute' -ShowWindow

TIPP - Cmdlet Erstellungsgrundgerüste (Template) über / per:
Visual Studio Code -> [CTRL]+[ALT]+[J] -> ex-cmdlet + [ENTER]
Visual Studio Code -> [CTRL]+[ALT]+[J] -> cmdlet + [ENTER]
Visual Studio Code -> Autovervollständigung -> ex-cmdlet + [TAB]
Visual Studio Code -> Autovervollständigung -> cmdlet + [TAB]
Cmdlet Erstellung per Builder im Internet

Eigene Cmdlets können auf zwei Arten erstellt werden. Entweder per .NET-Code z.B. C# oder VisualBasic.NET mittels Microsoft Visual Studio erstellt und zu einer .DLL-Datei kompiliert.

# Eine Übersicht der aktuell verfügbaren kompilierter Cmdlets.
Get-Command -CommandType 'Cmdlet' -Name '*-*'

Jedoch werde ich auf diese Art der Cmdlet Erstellung nicht weiter eingehen. Wer mehr wissen möchte den verweise ich auf den Artikel "How to write a cmdlet" oder folgt diesem einfachen Beispiel Creating a PowerShell Cmdlet Using C# and DotNet.exe.

Die weitaus charmantere zweite Art der Cmdlet Erstellung ist per PowerShell Script Language, um die es in diesem Artikel auch gehen wird.

# Eine Übersicht der aktuell verfügbaren PowerShell Script Language-Cmdlets.
Get-Command -CommandType 'Function' -Name '*-*'

Wie stark dominant eigentlich diese 2. Art ein Cmdlet zu erstellen, zeigt das folgende gruppierte Ergebnis:

Get-Command -CommandType 'Cmdlet', 'Function' -Name '*-*' | Group-Object -Property 'CommandType'

WICHTIG - Cmdlets die per PowerShell Script Language erstellt wurden müssen vor ihrer Verwendung in dem PowerShell-Laufwerk gci function:\ enthalten sein. Dies erfolgt jedesmal, wenn die Cmdlet-Funktions-Definition ausgeführt wird. Denken Sie auch daran, dass beim Beenden einer PowerShell-Session function:\ geleert wird und beim Starten einer neuen Session die Definition erneut ausgeführt werden muss. Einen Automatismus für das Laden von Cmdlets lässt sich per eigenem Modul erzeugen.

Die 4 Schritte der Cmdlet Erstellung

Anhand des folgenden Beispiels möchte ich diese 4 Schritte vorstellen. Ziel soll ein neues Cmdlet Get-About sein das die wichtigen PowerShell-About-Seiten übersichtlich darstellt (GUI) und per Auswahl (GUI) selektiert und anschließend diese Seiten öffnet (GUI).

1. Schritt - Eine statische Lösung ausarbeiten

Hier geht es darum das Problem bzw. den Auftrag mit Blick auf das Wesentliche per statischer Lösung zu entwickeln. Am Ende dieses Schrittes könnte der Code wie folgt aussehen:

# Statische Lösung
Get-Help -Name about_* | Sort-Object -Property Name | Out-GridView -Title 'Bitte about-Seiten auswählen (CTRL + Mausklick)' -OutputMode Multiple | Get-Help -ShowWindow

2. Schritt - Statische Lösung in eine Dynamische umwandeln

Nun wir der aktuelle Stand aus dem 1. Schritt in eine dynamische Lösung umgebaut. Ziel dabei ist die Verwendung von Variablen überall dort, wo später das Cmdlet von außen per Parameter gesteuert werden soll.

Achten Sie hierbei auf folgendes:

  • Nur Variablen verwenden die auch später als Parameter verwendet werden.
  • Variablen-Name so wählen wie auch später der Parameter heißen soll.
  • Erster Buchstabe der Variable großschreiben.

In unserem Beispie würde ein Parameter -Keyword hilfreich sein, um die Anzeige vor zu filtern:

# Dynamische Lösung
$Keyword = 'function'
Get-Help -Name "about_*$Keyword*" | Sort-Object -Property Name | Out-GridView -Title 'Bitte about-Seiten auswählen (CTRL + Mausklick)' -OutputMode Multiple | Get-Help -ShowWindow

3. Schritt - Dynamische Lösung in ein Cmdlet umwandeln

Die dynamische Lösung wird nun mit einem Cmdlet-Grundgerüst verdrahten. Mit steigender Erfahrung können Sie dieses Grundgerüst selbst reproduzieren und um Ihre dynamische Lösung legen. Bis dahin gibt es hervoragende Unterstützung von Visual Studio Code oder aus dem Internet:

  • Visual Studio Code -> [CTRL]+[ALT]+[J] -> ex-cmdlet + [ENTER]
  • Visual Studio Code -> [CTRL]+[ALT]+[J] -> cmdlet + [ENTER]
  • Visual Studio Code -> Autovervollständigung -> ex-cmdlet + [TAB]
  • Visual Studio Code -> Autovervollständigung -> cmdlet + [TAB]
  • Cmdlet Builder

Da Cmdlet erstellen/entwickeln ein kreativer schöpferischer Prozess ist, findet dieser 3. Schritt im Wechselspiel mit dem 4. Schritt statt.

Nach Abschluss dieses Schrittes würde unser Cmdlet wie folgt ausschauen:

# Cmdlet Lösung
function Get-About {
    <# .SYNOPSIS about-Seiten über eine GUI benutzen. .DESCRIPTION Die about-Seiten über eine GUI benutzen. .EXAMPLE Get-About Alle about-Seiten werden angezeigt .EXAMPLE Get-About -Keyword remote Alle about-Seiten werden angezeigt, die das Schlüsselwort Remote enthalten .PARAMETER Keyword Schlüsselwort nach dem die about-Seiten gefiltert werden. .INPUTS Nix .OUTPUTS Nix #>
    param (
        [string]$Keyword
    )
    Get-Help -Name "about_*$Keyword*" | Sort-Object -Property Name | Out-GridView -Title 'Bitte about-Seiten auswählen (CTRL + Mausklick)' -OutputMode Multiple | Get-Help -ShowWindow
}

Der Aufbau sowie die einzelnen Abschnitte eines Cmdlets können in den diversen About-Seiten nachgeschlagen werden:

  • Cmdlet-Hilfe für das PowerShell-Hilfesystem -> about_Comment_Based_Help
  • Grundlagen zur Cmdlet Erstellung -> about_Functions_Advanced
  • Verwendung des CmdletBinding-Attributes -> about_Functions_Advanced_Methods
  • Cmdlet-Parameter u.a. deren Validierung -> about_Functions_Advanced_Parameters
  • Cmdlet-Parameter bzgl. Pipeline-Verarbeitung -> about_Functions_CmdletBindingAttribute
  • Rückgabe-Objekte u.a. PSCustomObject. -> about_Functions_OutputTypeAttribute

TIPP - Sollte ein Objekt nicht die Daten oder Methoden enthalten die benötigt werden, können Sie PowerShell Objekte erweitern und diese im Anschluss zurückgeben.

4. Schritt - Cmdlet testen

Die Cmdlet Erstellung ist ein Wechselspiel zwischen Testen (Versuch und Irrtum) und der Anpassung des Cmdlet-Codes. Beachten Sie dabei folgende Empfehlungen:

  • Pro Cmdlet-Definition eine .PS1-Datei benutzen.
  • Aufbau dieser .PS1-Datei: zuerst Cmdlet Erstellungs-Code anschließend der Test-Code. Wenn das Testen abgeschlossen ist den Code nicht löschen sonder auskommentieren, um ihn später evtl. nochmal ausführen / testen zu können.
  • Code der für die aktuelle Entwicklungs- / Testphase nicht benötigt wird ist zu deaktivieren.
  • Testen Sie die erfolgreiche Benutzung aber auch die Fehlbedienung des Cmdlets.

Denken Sie daran, dass der Test-Code nur den aktuellen Cmdlet-Stand im function:\-Laufwerk testet. Haben Sie jedoch Änderungen im Code vorgenommen ohne function:\Get-About zu aktualisieren bleiben diese vom Test unberührt. Daher führen Sie die Datei immer per [F5] komplett aus.

MERKE - Selektion der Definition + [F8] und Selektion des Test-Codes + [F8] => NERVT => LÖSUNG: Je Cmdlet eine .PS1-Datei mit nur noch dem Code aus 3. + eine Test-Zeile und mit [F5] die Datei ausführen!

Wenn Sie später sattelfest Cmdlets erstellen können ist der nächste konsequente Schritt sich mit automatisierten Komponententest (Unittest, UTest) zu beschäftigen. Hierfür steht das PowerShell Modul Pester bereit (Pester-Projekt-Seite, PowerShellGallery/Pester).

# Positive Tests
Get-About
Get-About -Keyword remote
Get-Help -Name 'Get-About' -ShowWindow
Show-Command -Name 'Get-About' -NoCommonParameter -ErrorPopup

-WhatIf und -Confirm implementieren

Der bekannte Sinn und die Funktionalität der Common-Parameter -WhatIf und -Confirm können Sie bei Bedarf in Ihrem Cmdlet erstellen. Da es sich aber um allgemeingültige Parameter handelt, sollten Sie diesen Schritt nicht zu weit in die Zukunft legen.

Cmdlet-Ausführung bestätigen lassen

Bei -WhatIf bieten Sie dem Cmdlet-Benutzer die Möglichkeit die Ausführung zu simulieren ohne das tatsächlich etwas umgesetzt wird.

Bei -Confirm definieren Sie eine Risikostufe und bieten die Möglichkeit der Einzelbestätigung je Pipeline-Objekt an. Folgende Risikostufen stehen zur Verfügung:

Einstellung Beschreibung Intention
High Risikostufe Gefährlich ein Undo ist nicht möglich
Medium Risikostufe Durchschnittlich ein Undo bedingt unmöglich
Low Risikostufe Ungefährlich ein Undo möglich
None Risikostufe unbekannt ein Undo unbekannt

Weitere Details können Sie in der About-Seite Get-Help about_Functions_Advanced_Methods -s nachschlagen. Und das folgende Beispiel verdeutlicht die Implementierung:

# -WhatIf & -Confirm implementieren:
function Test-WhatIfConfirm {
    [CmdletBinding(SupportsShouldProcess = $true,
                   ConfirmImpact         = [System.Management.Automation.ConfirmImpact]::High)]
    param()

    $target = Get-Service -Name AudioSrv
    $action = [System.ServiceProcess.ServiceControllerStatus]::Stopped
    $canDo = $PSCmdlet.ShouldProcess($target.Name, $action)
    if($canDo) {
        $target | Stop-Service -Force -PassThru
    }
}

# ! Der AudioSrv-Dienst wird nach Nachfrage gestoppt (ConfirmImpact = High):
Test-WhatIfConfirm

# ! Der AudioSrv-Dienst wird umgehend gestoppt:
Test-WhatIfConfirm -Confirm:$false

# ! Das Stoppen des AudioSrv-Dienstes wird simuliert:
Test-WhatIfConfirm -WhatIf

# ! Der AudioSrv-Dienst wird umgehend gestoppt:
$ConfirmPreference = [System.Management.Automation.ConfirmImpact]::None
Test-WhatIfConfirm

# ! Der AudioSrv-Dienst wird nach Nachfrage gestoppt:
$WhatIfPreference = $true
Test-WhatIfConfirm

Parameter-Autovervollständigung

Einige Cmdlet-Parameter dürfen nur Werte einer vorher definierten List annehmen. Werte, die nicht in der Liste enthalten sind, können von der PowerShell akzeptiert oder mit einem Fehler abgewiesen werden, bevor das Cmdlet seine Arbeit aufnimmt. Als Sahnehäubchen bekommt der Cmdlet-Benutzer diese Werte in der Autovervollständigungsliste ([CTRL] + [SPACE]) angezeigt.

Für diese Überprüfung bzw. Autovervollständigung stehen folgende Möglichkeiten zur Verfügung:

  • Per Parameter-Typenfestlegung eines vorhandenen Datentyps [System.Management.Automation.JobState]
  • Per Parameter-Attribut ValidateSet
  • Per Parameter-Typenfestlegung eines eigenen Datentyps (z.B. [Enum]-Liste)
  • Per Parameter-Attribut ArgumentCompleter. Das Besondere bei diesem Verfahren ist, das neben der Liste auch andere Werte zugelassen sind.
  • Hinzufügen eines dynamicparam { }-Blocks.
# Enum-Listen-Typ erzeugen
enum Richtung {
    Unbekannt
    West
    Nord
    Ost
    Süd
}

function Test-ParameterAutoVervollständigung {
    param (
        # Per Parameter-Typenfestlegung eines vorhandenen Datentyps [ActionPreference]
        [System.Management.Automation.ActionPreference]$ParameterA,

        # Per Parameter-Attribut ValidateSet [ValidateSet("Erde", "Mond", "Sonne", "Mars")] [String]$ParameterB, # Per Parameter-Typenfestlegung eines eigenen Datentyps (z.B. [Enum]-Liste) [Richtung]$ParameterC, # Per Parameter-Attribut ArgumentCompleter [ArgumentCompleter({'Microsoft','Amazon','Google'})] [string] $ParameterD # Dynamische Parameter finden Sie in einem Kapitel weiter unten. ) }

Am besten testen Sie die Parameter-Autovervollständigung in einer Konsole per [CTRL] + [SPACE]. Schauen Sie sich auch die Möglichkeiten von Show-Command -Name 'Test-ParameterAutoVervollständigung' an. So werden Sie schnell die Vor- und Nachteile für Sich ausmachen.

Proxy-Cmdlet erstellen

Ein Proxy-Cmdlet wird anstelle dem original Cmdlet ausgeführt mit dem Mehrwert das Parameter für den Benutzer wegfallen oder vorbelegt werden. Wenn Sie ein Proxy-Cmdlet erstellen können Sie zusätzlichen Logik-Code hinzugefügt ohne alles neu entwickeln zu müssen. Hierzu brauchen Sie nur die folgenden zwei Zeilen Code ausführen und erhalten in die Zwischenablage den Proxy-Cmdlet-Code z.B. für Get-FileHash den Sie nach belieben einfügen ([CTRL]+[V]) und anpassen können.

$metadata = New-Object -TypeName 'System.Management.Automation.CommandMetadata' -ArgumentList (Get-Command -Name 'Get-FileHash')
[System.Management.Automation.ProxyCommand]::Create($metadata) | Set-Clipboard

Cmdlet mit dynamische Parameter versehen

In Abhängigkeit von bereits ausgefülltem statischem Parameter können "durch Geisterhand" neue Parameter in der Autovervollständigung erscheinen. Diese nennt man dynamisch Parameter und werden einem Cmdlet programmatisch hinzugefügt.

Zum Beispiel erhalten Sie den SwitchParameter -File, wenn Sie beim Parameter -Path ein Laufwerk vom PowerShell Provider FileSystem angeben:

Get-ChildItem -Path C:\ -File

Verwenden Sie jedoch ein Laufwerk vom PowerShell Provider Certificate steht -File nicht zur Verfügung jedoch der SwitchParameter -CodeSigningCert:

Get-ChildItem -Path Cert:\ -CodeSigningCert

Dies Parameter -File und -CodeSigningCert wurde mittels der Technik von dynamischen Parametern ermöglicht.

WICHTIG - Für die Autovervollständigung und den damit verbunden anzuzeigenden Werten ist es wichtig das die statischen Parameter physisch tippen, denn nur so werden die dynamischen Parameter aktiv.

In dem folgenden Beispiel sehen Sie Implementierung von dynamischen Parametern. Wird dem Parameter -Mode das Argument Normal Übergeben zeigt die Autovervollständigung keine weiteren Parameter an. Weißen Sie dem Parameter -Mode das Argument Dynamic werden dynamisch zwei weitere Parameter -Id und -Planet hinzugefügt.

function Test-DynamicParameter {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateSet('Normal', 'Dynamic')]
        [string]$Mode
    )

    dynamicParam {
        $parameters = New-Object -TypeName 'System.Management.Automation.RuntimeDefinedParameterDictionary'

        if ($Mode -ceq "Dynamic") {
            # 1. dynamischer Parameter (-Id)
            $parameterAttribute = New-Object -TypeName 'System.Management.Automation.ParameterAttribute'
            $parameterAttribute.Mandatory = $true
            $attributes = New-Object -TypeName 'System.Collections.ObjectModel.Collection[System.Attribute]'
            $attributes.Add($parameterAttribute)
            $parameter = New-Object -TypeName 'System.Management.Automation.RuntimeDefinedParameter' -ArgumentList 'Id', 'System.Int32', $attributes
            $parameters.Add('Id', $parameter)

            # 2. dynamischer Parameter (-Planet)
            $parameterAttribute = [System.Management.Automation.ParameterAttribute]::new()
            $parameterAttribute.Mandatory = $false
            $attributes = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
            $attributes.Add($parameterAttribute)
            $validateSetAttribute = [System.Management.Automation.ValidateSetAttribute]::new('Erde', 'Mond', 'Sonne', 'Mars') # TODO z.B. Werte aus dem AD, Exchange, Internet generieren
            $attributes.Add($validateSetAttribute)
            $parameter = [System.Management.Automation.RuntimeDefinedParameter]::new('Planet', 'System.String', $attributes)
            $parameters.Add('Planet', $parameter)
        }

        return $parameters
    }
    begin {}
    process {
        "Festgelegt wurde: Plant = $($PSBoundParameters.Planet) | ID = $($PSBoundParameters.Id)"
    }
    end {}
}

Die Parameter -Id und -Planet wurden nicht dynamisch hinzugefügt und stehen daher nicht zur Verfügung:

# ! Fehler => Test-DynamicParameter: A parameter cannot be found that matches parameter name 'Id'.
Test-DynamicParameter -Mode Normal -Id 4711 -Planet Mond

Die Parameter -Id und -Planet wurden dynamisch hinzugefügt und stehen daher zur Verfügungen AUCH in der Autovervollständigung:

Test-DynamicParameter -Mode Dynamic -Id 4711 -Planet Mond

ACHTUNG - die PowerShell führt den dynamicparam { }-Bereich automatisch und oft auf daher beachte Sie die Performance evtl. Daten zwischenspeichern.

function Test-DynamicSoundParameter {
    [CmdletBinding()]
    param()
    dynamicparam {

        $player = [System.Media.SoundPlayer]::new('C:\Windows\media\tada.wav')
        $player.Play()
    }
}
# ! Z.B. führen folgende Aktionen / Techniken den dynamicparam-Block aus und Sie hören einen Sound:
Test-Dynamic # Tab-Vervollständigung
Test-DynamicSoundParameter - # [CTRL] + [SPACE]
Get-Help 'Test-DynamicSoundParameter' # [ENTER]
Get-Command Test-DynamicSoundParameter -Syntax # [ENTER]
# etc.

Lernen von vorhanden Cmdlets anderer Programmierer

Wussten Sie das D: in der PowerShell eine function ist. Schauen wir uns einmal den Code dazu an:

# ? Wie wurde die Funktion/Cmdlet erstellt:
Get-Command -Name D: | Select-Object -Property Name, Definition

# ? Welche Funktionen enthalten den gleichen Code:
Get-ChildItem -Path Function:\ | Where-Object ScriptBlock -Match 'Set-Location \$MyInvocation\.MyCommand\.Name' | Select-Object -Property Name, ScriptBlock

Oder schauen wir uns mal den Code von New-Guid an:

Get-ChildItem -Path function:\New-Guid | Select-Object -ExpandProperty ScriptBlock
[GUID]::NewGuid() # ? Ah-ha, das führt das Cmdlet New-Guid aus.

ACHTUNG - wie Sie sehen können ist jeder Code einsehbar daher haben Passwörter oder andere Geheimnisse im Code nichts verloren!

Schauen Sie sich auch den Code der Cmdlets von meinem Modul AKPT an (Diese finden Sie im Unterordner Public):

Cmdlet Code lesen und verstehen

# Cmdlet Erstellungsbeispiele im AKPT/Public/*-Modul-Ordner:
Install-Module -Name AKPT

TIPP - Beim Cmdlet Erstellungs-Prozess wird man mit vielen unterschiedlichen Objekten konfrontiert. Daher sollte man beim Analysieren von Objekten sattelfest sein. Zum Thema Objekt-Analyse können Sie meinen Artikel: "PowerShell-Objekte in 3 Schritten erfolgreich analysieren" lesen.

Übungen zum Thema Cmdlet Erstellung

  1. Cmdlet-Code lesen & verstehen
    1. Lesen Sie das Cmdlet AKPT\Get-Product und erklären Sie es.
  2. Cmdlet-Code lesen & verstehen
    1. Lesen Sie das Cmdlet AKPT\Get-EuroExchange und erklären Sie es!
  3. Cmdlet Erstellen: Get-Hello
    1. Folgende Ausführungen sollen positive Ergebnisse auslösen:
      1. Get-Hello -Name "Peter" -Culture "DE" # => Hallo Peter!
      2. Get-Hello -Name "Peter" -Culture "US" # => Hello Peter!
      3. Get-Hello -Name "Peter" -Culture "SP" # => Ola Peter!
      4. Get-Hello -Name "Peter" # Default: DE d.h. => Hallo Peter!
      5. "Peter", "Inge" | Get-Hello -Culture "SP" # => Ola Peter! Ola Inge!
    2. Folgende Ausführungen sollen auf einen Fehler laufen:
      1. Get-Hello -Culture "SP" # ! => Fehler!
      2. Get-Hello # ! => Fehler!
      3. Get-Hello -Name "Peter" -Culture "XX" # ! Fehler!
      4. Get-Hello -Name "" # ! => Fehler
      5. Get-Hello -Name "Peter", "Inge" # ! => Fehler
  4. Cmdlet Erstellen: Add-DateTime
    1. Von einem DateTime-Objekt (z.B. Get-Date) eine Anzahl an Zahlen dazu zu addieren für Jahr, Monat und Tag.
    2. Folgende Syntax soll implementiert werden:
      1. Add-DateTime -BaseDateTime (Get-Date) -Years 1
      2. Add-DateTime -BaseDateTime (Get-Date) -Months 2
      3. Add-DateTime -BaseDateTime (Get-Date) -Days 3
      4. Add-DateTime -BaseDateTime (Get-Date) -Years 1 -Months 2 -Days 3
      5. Add-DateTime -BaseDateTime (Get-Date) -Years 1 -Days 3
      6. Get-Help -Name Add-DateTime -ShowWindow # ? Hilfe-Information i.O.
      7. Show-Command -Name Add-DateTime -ErrorPopup -NoCommonParameter # ? Bedienbar
    3. TIPPS - (Get-Date).AddYears(1); (Get-Date).Month(2); (Get-Date).Day(3)
  5. Cmdlet Erstellen: New-LocalUserFromCsv
    1. Neue lokale Windows-Benutzer über eine .CSV-Datei zu erstellen.
    2. Als Basis dient folgender Code:
      1. Get-ChildItem -Path 'C:\Temp\NewUsers.csv' | Get-Content | ConvertFrom-Csv -Delimiter ';' -Header 'Name', 'Password', 'Description' | Select-Object -Skip 1 -Property 'Name', 'Description', @{Label='Password'; Expression={ $_.Password | ConvertTo-SecureString -AsPlainText -Force }} | New-LocalUser -WhatIf
    3. Folgende Syntax soll implementiert werden:
      1. New-LocalUserFromCsv -CsvFile C:\Temp\NewUsers.csv -WhatIf # simuliert das Erstellen => [switch]$WhatIf
      2. New-LocalUserFromCsv -CsvFile C:\Temp\NewUsers.csv # erstellt die Benutzer
      3. New-LocalUserFromCsv # ! FEHLER da der Parameter 'CsvDatei' obligatorisch sein muss
      4. Get-Help -Name New-LocalUserFromCsv -ShowWindow # ? Hilfe-Information i.O.
      5. Show-Command -Name New-LocalUserFromCsv -ErrorPopup -NoCommonParameter # ? Bedienbar
  6. Cmdlet Erstellen: Get-EmptyDirectory
    1. Leere Ordner in Form von DirectoryInfo-Objekten zurückzugeben.
    2. ? Folgende Syntax soll implementiert werden:
      1. Get-EmptyDirectory -Path c:\temp
      2. Get-EmptyDirectory # Aktueller Ordner
      3. Get-EmptyDirectory -Path c:\temp -Recurse
      4. Get-EmptyDirectory -Path Z:\DiesenOrdnerGibEsNicht # FEHLER ausgeben da der Ordner nicht vorhanden ist!
      5. Get-Help -Name Get-EmptyDirectory -ShowWindow # Hilfe-Information i.O.?
      6. Show-Command -Name Get-EmptyDirectory -ErrorPopup -NoCommonParameter # bedienbar?