# PowerShell Sprache & Plattform PowerShell Language & Platform

Umfassende Sprachreferenz für PowerShell 5.1 und PowerShell 7 – von Grundlagen und Kontrollfluss über erweiterte Funktionen, Module, Klassen, Remoting, JEA und DSC bis zu Best Practices und Entwicklungsumgebungen. Comprehensive language reference for PowerShell 5.1 and PowerShell 7 – from fundamentals and control flow through advanced functions, modules, classes, remoting, JEA, and DSC to best practices and development environments.

PS 7 PS 7
Cross-Platform Cross-platform
PS 5.1 PS 5.1
Windows built-in Windows built-in
DSC DSC
Config as Code Config as code
JEA JEA
Least Privilege Least privilege
💡 PowerShell 7 ist die empfohlene Version für neue Projekte 💡 PowerShell 7 is the recommended version for new projects

PowerShell 7 (basierend auf .NET 8+) läuft auf Windows, Linux und macOS, unterstützt Parallelverarbeitung mit ForEach-Object -Parallel und bringt moderne Sprachkonstrukte. PowerShell 5.1 ist in Windows enthalten und bleibt für WMI-Cmdlets und Windows-only-Module relevant. PowerShell 7 (built on .NET 8+) runs on Windows, Linux, and macOS, supports parallel processing with ForEach-Object -Parallel, and brings modern language constructs. PowerShell 5.1 is built into Windows and remains relevant for WMI cmdlets and Windows-only modules.

Versionen Versions

5.1 vs. 7.x, .NET-Basis, Support-Matrix 5.1 vs. 7.x, .NET base, support matrix

Installation Installation

Windows, Linux, macOS, Container Windows, Linux, macOS, containers

Sprachgrundlagen Language fundamentals

Variablen, Typen, Arrays, Hashtables Variables, types, arrays, hashtables

Kontrollfluss Control flow

if, switch, for, foreach, while if, switch, for, foreach, while

Funktionen & Cmdlets Functions & cmdlets

Advanced Functions, CmdletBinding, Parameter Advanced functions, CmdletBinding, parameters

Module Modules

Erstellen, Manifest, PSGallery Create, manifest, PSGallery

Klassen & OOP Classes & OOP

PS-Klassen, Vererbung, Interfaces PS classes, inheritance, interfaces

Remoting & JEA Remoting & JEA

PSSession, WinRM, Just Enough Administration PSSession, WinRM, Just Enough Administration

## Versionen & .NET-Basis Versions & .NET base

PowerShell existiert in zwei aktiven Hauptlinien: die in Windows integrierte Version 5.1 (basierend auf .NET Framework 4.x) und die plattformübergreifende PowerShell 7-Reihe (basierend auf .NET 6/7/8). Beide koexistieren problemlos auf demselben System. PowerShell exists in two active main lines: the Windows-built-in version 5.1 (based on .NET Framework 4.x) and the cross-platform PowerShell 7 line (based on .NET 6/7/8). Both coexist without issues on the same system.

Version Version | .NET-Basis.NET base | Plattform Platform | Status Status | Besonderheiten Highlights
--- | --- | --- | --- | ---
5.1 5.1 | .NET Framework 4.8.NET Framework 4.8 | Windows only Windows only | Wartungsmodus Maintenance mode | WMI, COM, vollständige Module-Kompatibilität WMI, COM, full module compatibility
7.2 (LTS)7.2 (LTS) | .NET 6.NET 6 | Win / Linux / macOS Win / Linux / macOS | LTS bis Nov 2024 LTS until Nov 2024 | Stabil, breite Enterprise-Adoption Stable, broad enterprise adoption
7.4 (LTS)7.4 (LTS) | .NET 8.NET 8 | Win / Linux / macOS Win / Linux / macOS | LTS aktiv LTS active | Empfohlen, ForEach-Object -Parallel, null-Koaleszenz Recommended, ForEach-Object -Parallel, null coalescing
7.5 7.5 | .NET 9.NET 9 | Win / Linux / macOS Win / Linux / macOS | Aktuell Current | Neueste Features, kürzerer Support-Zyklus Latest features, shorter support cycle

ℹ️ Kompatibilitätsschicht ℹ️ Compatibility layer

PowerShell 7 enthält eine Windows PowerShell Compatibility-Schicht, die über das Modul `WindowsCompatibility` Windows-only-Module in einer impliziten 5.1-Session ausführt. Das ermöglicht die Nutzung von Modulen wie ActiveDirectory oder DISM aus PowerShell 7 heraus. PowerShell 7 includes a Windows PowerShell Compatibility layer that uses the `WindowsCompatibility` module to run Windows-only modules in an implicit 5.1 session. This allows modules such as ActiveDirectory or DISM to be used from PowerShell 7.

## Installation & Laufzeit Installation & runtime

PowerShell 7 lässt sich über MSI, winget, Homebrew, apt/dnf und als Container-Image installieren. Für CI/CD-Umgebungen empfiehlt sich das offizielle Docker-Image `mcr.microsoft.com/powershell`. PowerShell 7 can be installed via MSI, winget, Homebrew, apt/dnf, and as a container image. For CI/CD environments the official Docker image `mcr.microsoft.com/powershell` is recommended.

Methode Method | Befehl Command | Plattform Platform
--- | --- | ---
winget winget | `winget install --id Microsoft.PowerShell` `winget install --id Microsoft.PowerShell` | Windows Windows
MSI-Paket MSI package | GitHub Releases herunterladen Download from GitHub Releases | Windows Windows
Homebrew Homebrew | `brew install --cask powershell` `brew install --cask powershell` | macOS macOS
apt (Ubuntu)apt (Ubuntu) | `apt-get install -y powershell` `apt-get install -y powershell` | Linux Linux
Docker Docker | `docker run -it mcr.microsoft.com/powershell` `docker run -it mcr.microsoft.com/powershell` | Container Container
GitHub Actions GitHub Actions | `pwsh` vorinstalliert auf ubuntu-latest `pwsh` pre-installed on ubuntu-latest | CI/CD CI/CD

PowerShell PowerShell

    # Installation via winget (Windows)
    winget install --id Microsoft.PowerShell --source winget

    # Prüfen der installierten Version
    $PSVersionTable

    # Ubuntu/Debian: Microsoft-Repository einrichten und installieren
    curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
    sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-focal-prod focal main" > /etc/apt/sources.list.d/microsoft.list'
    sudo apt-get update
    sudo apt-get install -y powershell

    # Execution Policy für CurrentUser setzen (Windows)
    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

## Sprachgrundlagen Language fundamentals

PowerShell ist eine objektorientierte, dynamisch typisierte Skriptsprache. Variablen beginnen mit `$`, Typen können explizit angegeben werden, und alles ist ein Objekt – auch primitive Werte. Der Pipeline-Operator `|` übergibt Objekte zwischen Cmdlets. PowerShell is an object-oriented, dynamically typed scripting language. Variables start with `$`, types can be specified explicitly, and everything is an object – including primitive values. The pipeline operator `|` passes objects between cmdlets.

Konstrukt Construct | Beispiel Example | Hinweis Note
--- | --- | ---
Variable Variable | `$name = "Alice"``$name = "Alice"` | Automatische Typerkennung Automatic type inference
Typisierte Variable Typed variable | `[int]$count = 42``[int]$count = 42` | Casting wird erzwungen Casting is enforced
Array Array | `$arr = @(1, 2, 3)``$arr = @(1, 2, 3)` | Nullbasierter Index, `.Count` Zero-based index, `.Count`
Hashtable Hashtable | `$h = @{Key = "Value"}``$h = @{Key = "Value"}` | Key-Value-Zugriff via `$h.Key` Key-value access via `$h.Key`
Ordered Hashtable Ordered hashtable | `[ordered]@{A=1; B=2}``[ordered]@{A=1; B=2}` | Einfügereihenfolge erhalten Insertion order preserved
Here-String Here-string | `@"..."@``@"..."@` | Mehrzeiliger String mit Interpolation Multi-line string with interpolation
Null-Koaleszenz (PS7)Null coalescing (PS7) | `$x ?? "default"``$x ?? "default"` | Fallback wenn `$null` Fallback when `$null`
Ternary (PS7)Ternary (PS7) | `$x ? "yes" : "no"``$x ? "yes" : "no"` | Kurzform für if/else Shorthand for if/else

PowerShell PowerShell

    # Variablen und Typen
    [string]$displayName = "Alice Müller"
    [int]$age             = 32
    [bool]$isAdmin        = $false
    [datetime]$created    = Get-Date

    # Null-Koaleszenz und Ternary (PowerShell 7+)
    $city   = $null
    $result = $city ?? "Berlin"          # => "Berlin"
    $label  = $isAdmin ? "Admin" : "User" # => "User"

    # Arrays
    $colors = @("red", "green", "blue")
    $colors += "yellow"
    $colors | Sort-Object | Where-Object { $_ -ne "red" }

    # Hashtables
    $user = @{
        DisplayName = "Alice Müller"
        UPN         = "alice@contoso.com"
        Department  = "IT"
    }
    $user.Department  # => "IT"
    $user["UPN"]      # => "alice@contoso.com"

    # Here-String mit Interpolation
    $msg = @"
    Hallo $($user.DisplayName),
    Ihr Konto wurde am $created angelegt.
    "@
    Write-Output $msg

    # Typenoperatoren
    42 -is [int]         # True
    "hello" -as [int]    # $null (kein Fehler)

## Kontrollfluss Control flow

PowerShell bietet alle klassischen Kontrollflusskonstrukte. Das `switch`-Statement ist besonders mächtig – es unterstützt Wildcards, Regex und Dateipfade. `foreach` iteriert über Sammlungen, `ForEach-Object` verarbeitet Elemente in der Pipeline. PowerShell offers all classic control flow constructs. The `switch` statement is particularly powerful – it supports wildcards, regex, and file paths. `foreach` iterates over collections, `ForEach-Object` processes elements in the pipeline.

PowerShell PowerShell

    # if / elseif / else
    $score = 87
    if ($score -ge 90)       { "A" }
    elseif ($score -ge 75)   { "B" }
    elseif ($score -ge 60)   { "C" }
    else                     { "F" }

    # switch mit Regex und Wildcard
    $input = "ERROR: disk full"
    switch -Regex ($input) {
        "^ERROR"   { Write-Warning "Critical: $_"; break }
        "^WARN"    { Write-Warning "Warning: $_" }
        default    { Write-Verbose "Info: $_" }
    }

    # foreach-Schleife
    $servers = @("srv01", "srv02", "srv03")
    foreach ($srv in $servers) {
        Write-Host "Checking $srv..."
    }

    # Pipeline ForEach-Object mit Scriptblock
    $servers | ForEach-Object {
        $ping = Test-Connection -ComputerName $_ -Count 1 -Quiet
        [pscustomobject]@{ Server = $_; Online = $ping }
    }

    # ForEach-Object -Parallel (PowerShell 7+)
    $servers | ForEach-Object -Parallel {
        $ping = Test-Connection -ComputerName $_ -Count 1 -Quiet
        [pscustomobject]@{ Server = $_; Online = $ping }
    } -ThrottleLimit 5

    # while-Schleife mit break/continue
    $retries = 0
    while ($retries -lt 3) {
        $retries++
        try {
            Invoke-RestMethod -Uri "https://api.example.com/health" | Out-Null
            break
        } catch {
            Start-Sleep -Seconds 2
        }
    }

## Funktionen & erweiterte Cmdlets Functions & advanced cmdlets

Einfache Funktionen werden mit dem Schlüsselwort `function` definiert. Mit `[CmdletBinding()]` und `[Parameter(...)]`-Attributen entstehen fortgeschrittene Funktionen, die vollständig mit dem PowerShell-Cmdlet-Ökosystem interagieren – inklusive `-Verbose`, `-WhatIf`, `-Confirm` und Pipeline-Input. Simple functions are defined with the `function` keyword. Using `[CmdletBinding()]` and `[Parameter(...)]` attributes creates advanced functions that integrate fully with the PowerShell cmdlet ecosystem – including `-Verbose`, `-WhatIf`, `-Confirm`, and pipeline input.

Attribut Attribute | Zweck Purpose | Beispiel Example
--- | --- | ---
`[CmdletBinding()]``[CmdletBinding()]` | Aktiviert Cmdlet-Verhalten: Verbose, Debug, WhatIf Enables cmdlet behavior: Verbose, Debug, WhatIf | Immer für öffentliche Funktionen verwenden Always use for public functions
`[Parameter(Mandatory)]``[Parameter(Mandatory)]` | Pflichtparameter, Benutzer wird interaktiv gefragt Required parameter, user is prompted interactively | `[Parameter(Mandatory)]``[Parameter(Mandatory)]`
`[Parameter(ValueFromPipeline)]``[Parameter(ValueFromPipeline)]` | Erlaubt Pipeline-Input Allows pipeline input | `[Parameter(ValueFromPipeline=$true)]``[Parameter(ValueFromPipeline=$true)]`
`[ValidateSet()]``[ValidateSet()]` | Einschränkung auf erlaubte Werte Restrict to allowed values | `[ValidateSet("Dev","Test","Prod")]``[ValidateSet("Dev","Test","Prod")]`
`[ValidatePattern()]``[ValidatePattern()]` | Regex-Validierung des Eingabewerts Regex validation of input value | `[ValidatePattern("^[A-Z]{3}\d{4}$")]``[ValidatePattern("^[A-Z]{3}\d{4}$")]`
`[OutputType()]``[OutputType()]` | Dokumentiert den Rückgabetyp Documents the return type | `[OutputType([pscustomobject])]``[OutputType([pscustomobject])]`
`SupportsShouldProcess` `SupportsShouldProcess` | Aktiviert -WhatIf und -Confirm Enables -WhatIf and -Confirm | `[CmdletBinding(SupportsShouldProcess)]``[CmdletBinding(SupportsShouldProcess)]`

PowerShell PowerShell

    function Set-UserLicense {
        [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
        [OutputType([pscustomobject])]
        param(
            [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [ValidatePattern('^[\w\.\-]+@[\w\-]+\.[\w\.]+$')]
            [string]$UserPrincipalName,

            [Parameter(Mandatory)]
            [ValidateSet("E3","E5","F1","F3")]
            [string]$LicenseSku,

            [Parameter()]
            [switch]$Remove
        )

        begin {
            Write-Verbose "Connecting to Microsoft Graph..."
            Connect-MgGraph -Scopes "User.ReadWrite.All","Organization.Read.All" -NoWelcome
        }

        process {
            $user = Get-MgUser -UserId $UserPrincipalName -ErrorAction Stop
            $skuId = (Get-MgSubscribedSku | Where-Object SkuPartNumber -eq $LicenseSku).SkuId

            if ($PSCmdlet.ShouldProcess($UserPrincipalName, "Set license $LicenseSku")) {
                if ($Remove) {
                    Set-MgUserLicense -UserId $user.Id `
                        -AddLicenses @() `
                        -RemoveLicenses @($skuId)
                } else {
                    Set-MgUserLicense -UserId $user.Id `
                        -AddLicenses @{SkuId = $skuId} `
                        -RemoveLicenses @()
                }
                [pscustomobject]@{
                    User    = $UserPrincipalName
                    License = $LicenseSku
                    Action  = if ($Remove) { "Removed" } else { "Assigned" }
                    Success = $true
                }
            }
        }

        end {
            Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
            Write-Verbose "Disconnected from Microsoft Graph."
        }
    }

    # Verwendung via Pipeline
    "alice@contoso.com","bob@contoso.com" | Set-UserLicense -LicenseSku "E3" -Verbose

## Module Modules

Ein PowerShell-Modul ist eine Sammlung von Funktionen, Variablen und Ressourcen, die als Einheit geladen und verteilt werden. Das Modulmanifest (`.psd1`) definiert Metadaten, Abhängigkeiten, exportierte Elemente und die benötigte PowerShell-Version. A PowerShell module is a collection of functions, variables, and resources that are loaded and distributed as a unit. The module manifest (`.psd1`) defines metadata, dependencies, exported elements, and the required PowerShell version.

PowerShell PowerShell

    # Modul-Struktur anlegen
    New-Item -Path ".\ContosoUtils" -ItemType Directory
    New-Item -Path ".\ContosoUtils\ContosoUtils.psm1" -ItemType File
    New-Item -Path ".\ContosoUtils\Public"  -ItemType Directory
    New-Item -Path ".\ContosoUtils\Private" -ItemType Directory

    # Modul-Manifest erstellen
    New-ModuleManifest -Path ".\ContosoUtils\ContosoUtils.psd1" `
        -RootModule    "ContosoUtils.psm1" `
        -ModuleVersion "1.0.0" `
        -Author        "Contoso Team" `
        -Description   "Contoso Utility Functions for Microsoft 365" `
        -PowerShellVersion "7.0" `
        -RequiredModules @("Microsoft.Graph.Users") `
        -FunctionsToExport "*"

    # psm1: Public-Funktionen auto-importieren
    $publicFunctions = Get-ChildItem -Path "$PSScriptRoot\Public" -Filter "*.ps1"
    foreach ($fn in $publicFunctions) { . $fn.FullName }

    Export-ModuleMember -Function ($publicFunctions.BaseName)

    # Modul installieren und importieren
    Copy-Item -Path ".\ContosoUtils" -Destination "$env:PSModulePath\ContosoUtils" -Recurse
    Import-Module ContosoUtils -Verbose

    # Via PSGallery veröffentlichen
    Publish-Module -Path ".\ContosoUtils" -NuGetApiKey $env:PSGALLERY_API_KEY -Repository PSGallery

## Klassen & objektorientierte Programmierung Classes & object-oriented programming

Seit PowerShell 5.0 lassen sich Klassen mit `class` definieren. Klassen unterstützen Vererbung, Methoden, Eigenschaften, Konstruktoren und das Implementieren von .NET-Interfaces. Sie sind besonders nützlich für DSC-Ressourcen und komplexe Datenmodelle. Since PowerShell 5.0, classes can be defined using the `class` keyword. Classes support inheritance, methods, properties, constructors, and implementing .NET interfaces. They are especially useful for DSC resources and complex data models.

PowerShell PowerShell

    # Basisklasse
    class AzureResource {
        [string]$Name
        [string]$ResourceGroup
        [string]$Location
        hidden [datetime]$CreatedAt = (Get-Date)

        AzureResource([string]$name, [string]$rg, [string]$loc) {
            $this.Name          = $name
            $this.ResourceGroup = $rg
            $this.Location      = $loc
        }

        [string] GetId() {
            return "/subscriptions/{sub}/resourceGroups/$($this.ResourceGroup)/providers/Microsoft.Resources/$($this.Name)"
        }

        [string] ToString() {
            return "$($this.Name) [$($this.Location)]"
        }
    }

    # Abgeleitete Klasse
    class StorageAccount : AzureResource {
        [string]$Sku
        [bool]$HierarchicalNamespace

        StorageAccount([string]$name, [string]$rg, [string]$loc, [string]$sku) : base($name, $rg, $loc) {
            $this.Sku = $sku
            $this.HierarchicalNamespace = $false
        }

        [string] GetConnectionString() {
            return "DefaultEndpointsProtocol=https;AccountName=$($this.Name);EndpointSuffix=core.windows.net"
        }
    }

    # Verwendung
    $sa = [StorageAccount]::new("mystorageacct", "rg-prod", "westeurope", "Standard_LRS")
    $sa.ToString()
    $sa.GetConnectionString()

## Remoting (PSRemoting / WinRM) Remoting (PSRemoting / WinRM)

PowerShell Remoting ermöglicht die Ausführung von Befehlen auf entfernten Computern über das WinRM-Protokoll (Windows) oder SSH (plattformübergreifend in PS7). Persistente Sessions mit `New-PSSession` sind effizienter als einmalige `Invoke-Command`-Aufrufe. PowerShell Remoting allows executing commands on remote computers over the WinRM protocol (Windows) or SSH (cross-platform in PS7). Persistent sessions with `New-PSSession` are more efficient than one-time `Invoke-Command` calls.

Cmdlet Cmdlet | Zweck Purpose | Hinweis Note
--- | --- | ---
`Enable-PSRemoting` `Enable-PSRemoting` | WinRM aktivieren und konfigurieren Enable and configure WinRM | Als Administrator auf Zielcomputer Run as administrator on target
`New-PSSession` `New-PSSession` | Persistente Remote-Verbindung aufbauen Establish persistent remote connection | Objekte und Variablen bleiben erhalten Objects and variables persist
`Invoke-Command` `Invoke-Command` | Befehl auf Remote-Computer ausführen Run command on remote computer | Gleichzeitig auf mehrere Ziele Target multiple computers simultaneously
`Enter-PSSession` `Enter-PSSession` | Interaktive Remote-Shell Interactive remote shell | Für interaktive Diagnose For interactive diagnostics
`Copy-Item -ToSession` `Copy-Item -ToSession` | Datei zu Remote-Session kopieren Copy file to remote session | Kein Netzwerkfreigabe erforderlich No network share required

PowerShell PowerShell

    # Remoting auf Zielcomputer aktivieren (Einmalig als Admin)
    Enable-PSRemoting -Force

    # PSSession erstellen und wiederverwenden
    $cred = Get-Credential
    $sessions = New-PSSession -ComputerName srv01, srv02, srv03 -Credential $cred

    # Befehl auf alle Sessions gleichzeitig ausführen
    $results = Invoke-Command -Session $sessions -ScriptBlock {
        [pscustomobject]@{
            Host   = $env:COMPUTERNAME
            OS     = (Get-CimInstance Win32_OperatingSystem).Caption
            Uptime = (Get-Date) - (gcim Win32_OperatingSystem).LastBootUpTime
        }
    }
    $results | Format-Table -AutoSize

    # Datei in Remote-Session kopieren
    Copy-Item -Path ".\deploy.ps1" -Destination "C:\Deploy\deploy.ps1" `
        -ToSession $sessions[0]

    # Session-Verbindung über SSH (PowerShell 7+, plattformübergreifend)
    $sshSession = New-PSSession -HostName "linuxserver.contoso.com" `
        -UserName "adminuser" -SSHTransport
    Invoke-Command -Session $sshSession -ScriptBlock { uname -a }

    # Sessions schließen
    $sessions | Remove-PSSession

## JEA – Just Enough Administration JEA – Just Enough Administration

JEA schränkt PowerShell-Remote-Sessions auf einen minimalen Befehlssatz ein und lässt nicht-administrative Benutzer bestimmte administrative Aufgaben mit erhöhten Rechten erledigen. Die Konfiguration besteht aus einer Session Configuration File (`.pssc`) und einer oder mehreren Role Capability Files (`.psrc`). JEA restricts PowerShell remote sessions to a minimal command set and allows non-administrative users to perform specific administrative tasks with elevated rights. The configuration consists of a session configuration file (`.pssc`) and one or more role capability files (`.psrc`).

PowerShell PowerShell

    # Role Capability File erstellen
    New-Item -Path "C:\JEA\RoleCapabilities" -ItemType Directory -Force
    New-PSRoleCapabilityFile -Path "C:\JEA\RoleCapabilities\DnsAdmin.psrc"

    # DnsAdmin.psrc: nur DNS-Cmdlets erlauben
    @{
        VisibleCmdlets = @(
            'Resolve-DnsName',
            @{Name = 'Set-DnsServerResourceRecord'; Parameters = @{Name = 'ZoneName'; ValidateSet = 'contoso.com'}}
        )
        VisibleFunctions = 'TabExpansion2'
        VisibleExternalCommands = @()
    }

    # Session Configuration File erstellen
    New-PSSessionConfigurationFile -Path "C:\JEA\JEA_DNS.pssc" `
        -SessionType RestrictedRemoteServer `
        -RunAsVirtualAccount `
        -TranscriptDirectory "C:\JEA\Transcripts" `
        -RoleDefinitions @{
            'CONTOSO\HelpDesk' = @{ RoleCapabilities = 'DnsAdmin' }
        }

    # Session-Konfiguration registrieren
    Register-PSSessionConfiguration -Name "JEA_DNS" `
        -Path "C:\JEA\JEA_DNS.pssc" -Force

    # Zugriff über JEA-Endpunkt
    Enter-PSSession -ComputerName dns01 -ConfigurationName JEA_DNS `
        -Credential (Get-Credential "CONTOSO\helpdesk1")

## Desired State Configuration (DSC) Desired State Configuration (DSC)

DSC ist ein deklaratives Framework, das den Zielzustand von Windows-Systemen beschreibt und bei Bedarf durchsetzt. Konfigurationen werden in PowerShell-Syntax definiert, in MOF-Dokumente kompiliert und durch den Local Configuration Manager (LCM) angewendet. DSC v3 (plattformübergreifend) läuft auf PowerShell 7. DSC is a declarative framework that describes the desired state of Windows systems and enforces it when required. Configurations are defined in PowerShell syntax, compiled to MOF documents, and applied by the Local Configuration Manager (LCM). DSC v3 (cross-platform) runs on PowerShell 7.

PowerShell PowerShell

    # DSC-Modul installieren
    Install-Module PSDesiredStateConfiguration -RequiredVersion 2.0.7 -Force

    # Einfache DSC-Konfiguration
    configuration WebServerConfig {
        param([string[]]$ComputerName = 'localhost')

        Import-DscResource -ModuleName PSDesiredStateConfiguration

        Node $ComputerName {
            WindowsFeature IIS {
                Ensure = "Present"
                Name   = "Web-Server"
            }

            WindowsFeature AspNet {
                Ensure    = "Present"
                Name      = "Web-Asp-Net45"
                DependsOn = "[WindowsFeature]IIS"
            }

            File DefaultPage {
                Ensure          = "Present"
                Type            = "File"
                DestinationPath = "C:\inetpub\wwwroot\index.html"
                Contents        = "Deployed via DSC"
                DependsOn       = "[WindowsFeature]IIS"
            }

            Service W3SVC {
                Name      = "W3SVC"
                State     = "Running"
                DependsOn = "[WindowsFeature]IIS"
            }
        }
    }

    # Kompilieren und anwenden
    WebServerConfig -ComputerName "webserver01" -OutputPath ".\WebConfig"
    Start-DscConfiguration -Path ".\WebConfig" -ComputerName "webserver01" -Verbose -Wait -Force

    # Zustand prüfen
    Test-DscConfiguration -ComputerName "webserver01"
    Get-DscConfigurationStatus -ComputerName "webserver01"

## Fehlerbehandlung Error handling

PowerShell unterscheidet zwischen terminating und non-terminating Fehlern. Non-terminating Fehler werden mit `-ErrorAction Stop` zu terminating Fehlern konvertiert. Das `$Error`-Array, `$?` und `$LASTEXITCODE` liefern Zustandsinformationen. Strukturiertes Logging kann mit `Write-Error`, `Write-Warning` und Transcript-Logging kombiniert werden. PowerShell distinguishes between terminating and non-terminating errors. Non-terminating errors are converted to terminating errors using `-ErrorAction Stop`. The `$Error` array, `$?`, and `$LASTEXITCODE` provide state information. Structured logging can be combined with `Write-Error`, `Write-Warning`, and transcript logging.

PowerShell PowerShell

    function Invoke-SafeGraphCall {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory)][string]$Uri,
            [Parameter()][int]$MaxRetries = 3
        )

        $attempt = 0
        do {
            $attempt++
            try {
                $response = Invoke-MgGraphRequest -Method GET -Uri $Uri -ErrorAction Stop
                return $response
            }
            catch [Microsoft.Graph.PowerShell.AuthenticationException] {
                Write-Error "Authentication failed – check token: $($_.Exception.Message)"
                throw  # nicht wiederholbar
            }
            catch [System.Net.Http.HttpRequestException] {
                if ($_.Exception.StatusCode -eq 429) {
                    $retryAfter = [int]($_.Exception.Response.Headers['Retry-After'] ?? 10)
                    Write-Warning "Throttled. Warte $retryAfter Sekunden... (Versuch $attempt/$MaxRetries)"
                    Start-Sleep -Seconds $retryAfter
                } else {
                    Write-Warning "HTTP-Fehler $($_.Exception.StatusCode): $($_.Exception.Message) (Versuch $attempt)"
                    Start-Sleep -Seconds (2 * $attempt)
                }
            }
            catch {
                $err = $_
                Write-Warning "Unbekannter Fehler (Versuch $attempt): $($err.Exception.Message)"
                Write-Verbose "StackTrace: $($err.ScriptStackTrace)"
                Start-Sleep -Seconds 2
            }
        } while ($attempt -lt $MaxRetries)

        throw "API-Aufruf nach $MaxRetries Versuchen fehlgeschlagen: $Uri"
    }

    # Fehlerbehandlung mit -ErrorVariable und ErrorAction
    $result = Get-MgUser -UserId "notexist@contoso.com" `
        -ErrorAction SilentlyContinue `
        -ErrorVariable graphError
    if ($graphError) {
        Write-Warning "Benutzer nicht gefunden: $($graphError[0].Exception.Message)"
    }

    # Transcript für Audit-Logging
    Start-Transcript -Path ".\runlog_$(Get-Date -Format yyyyMMdd_HHmmss).txt" -Append
    # ... Skript-Aktionen ...
    Stop-Transcript

⚠️ ErrorActionPreference in Skripten setzen ⚠️ Set ErrorActionPreference in scripts

Setze `$ErrorActionPreference = 'Stop'` am Anfang von Produktionsskripten, um sicherzustellen, dass alle Fehler als terminierende Fehler behandelt und von try/catch abgefangen werden. Ohne diese Einstellung laufen Skripte bei nicht-terminierenden Fehlern still weiter. Set `$ErrorActionPreference = 'Stop'` at the start of production scripts to ensure all errors are treated as terminating errors and caught by try/catch. Without this setting, scripts silently continue on non-terminating errors.

## Best Practices Best practices

Professionelle PowerShell-Entwicklung folgt etablierten Konventionen hinsichtlich Benennung, Sicherheit, Lesbarkeit und Testbarkeit. Das PowerShell Best Practices and Style Guide (PSSA) kann durch das Modul `PSScriptAnalyzer` automatisch überprüft werden. Professional PowerShell development follows established conventions for naming, security, readability, and testability. The PowerShell Best Practices and Style Guide (PSSA) can be automatically checked using the `PSScriptAnalyzer` module.

Bereich Area | Empfehlung Recommendation | Begründung Rationale
--- | --- | ---
Benennung Naming | Verb-Noun mit genehmigten Verben (`Get-Verb`)Verb-Noun with approved verbs (`Get-Verb`) | Konsistenz mit dem PS-Ökosystem Consistency with the PS ecosystem
Secrets Secrets | Nie Passwörter im Code; `SecretManagement` oder Key Vault verwenden Never hardcode passwords; use SecretManagement or Key Vault | Sicherheit und Auditierbarkeit Security and auditability
Typen Types | Parameter-Typen immer angeben Always specify parameter types | Frühe Fehlererkennung, besseres IntelliSense Early error detection, better IntelliSense
Linting Linting | `Invoke-ScriptAnalyzer` im CI integrieren Integrate `Invoke-ScriptAnalyzer` in CI | Einheitliche Codequalität im Team Consistent code quality across the team
Testen Testing | Pester 5 für Unit- und Integration-Tests Pester 5 for unit and integration tests | Regression- und CI/CD-Gates Regression and CI/CD gates
Pipeline-Output Pipeline output | Objekte ausgeben, nie Strings parsen Output objects, never parse strings | Downstream-Kompatibilität und Filterung Downstream compatibility and filtering
Module versionen Module versions | `-RequiredVersion` in CI-Umgebungen`-RequiredVersion` in CI environments | Reproduzierbare Builds Reproducible builds
Parallelität Parallelism | `ForEach-Object -Parallel` mit -ThrottleLimit `ForEach-Object -Parallel` with -ThrottleLimit | Performance bei Massen-Operationen Performance for bulk operations

PowerShell PowerShell

    # PSScriptAnalyzer installieren und ausführen
    Install-Module PSScriptAnalyzer -Scope CurrentUser -Force
    Invoke-ScriptAnalyzer -Path ".\MyScript.ps1" -Severity Warning,Error

    # Pester 5: Beispieltest
    Install-Module Pester -MinimumVersion 5.0 -Scope CurrentUser -Force
    Import-Module Pester

    Describe "Get-UserReport" {
        BeforeAll {
            . "$PSScriptRoot\Get-UserReport.ps1"
            Mock Get-MgUser { @([pscustomobject]@{DisplayName="Test User"; UserPrincipalName="test@contoso.com"}) }
        }

        It "gibt mindestens einen Benutzer zurück" {
            $result = Get-UserReport -Filter "department eq 'IT'"
            $result.Count | Should -BeGreaterThan 0
        }

        It "enthält UserPrincipalName-Eigenschaft" {
            $result = Get-UserReport -Filter "department eq 'IT'"
            $result[0].UserPrincipalName | Should -Not -BeNullOrEmpty
        }
    }

    # SecretManagement: Key Vault als Secret Store
    Install-Module Microsoft.PowerShell.SecretManagement -Scope CurrentUser
    Install-Module Az.KeyVault -Scope CurrentUser
    Register-SecretVault -Name "ContosoVault" -ModuleName Az.KeyVault `
        -VaultParameters @{ AZKVaultName = "contosokeyvault"; SubscriptionId = $subId }
    $apiKey = Get-Secret -Name "GraphApiKey" -AsPlainText

## ISE vs. Visual Studio Code ISE vs. Visual Studio Code

Die PowerShell ISE (Integrated Scripting Environment) ist in Windows enthalten, wird aber nicht mehr aktiv weiterentwickelt und unterstützt weder PowerShell 7 noch plattformübergreifende Szenarien. Visual Studio Code mit der PowerShell-Extension ist die empfohlene Entwicklungsumgebung. The PowerShell ISE (Integrated Scripting Environment) is built into Windows but is no longer actively developed and supports neither PowerShell 7 nor cross-platform scenarios. Visual Studio Code with the PowerShell extension is the recommended development environment.

Feature Feature | PowerShell ISE PowerShell ISE | VS Code + PS Extension VS Code + PS Extension
--- | --- | ---
PS-Version PS version | 5.1 only 5.1 only | 5.1 + 7.x 5.1 + 7.x
Plattform Platform | Windows only Windows only | Win / Linux / macOS Win / Linux / macOS
IntelliSense IntelliSense | Grundlegend Basic | Vollständig, inkl. Parameterinfo Full, including parameter info
Debugging Debugging | Breakpoints, Watch Breakpoints, Watch | Breakpoints, Watch, Conditional, Logpoints Breakpoints, Watch, Conditional, Logpoints
Git-Integration Git integration | Keine None | Vollständig integriert Fully integrated
Extensions Extensions | Keine None | Marketplace mit tausenden Extensions Marketplace with thousands of extensions
Remote Development Remote development | Nein No | SSH, WSL, Container, Codespaces SSH, WSL, Container, Codespaces
Pester-Integration Pester integration | Manuell Manual | Test Explorer Extension Test Explorer extension
Wartungsstatus Maintenance status | Eingestellt (keine neuen Features)Discontinued (no new features) | Aktiv weiterentwickelt Actively developed

## VS Code Einrichtung für PowerShell VS Code setup for PowerShell

Die VS Code PowerShell-Extension bietet Debugging, IntelliSense, integriertes Terminal und direkte Pester-Test-Ausführung. Eine `settings.json`-Konfiguration in `.vscode/` legt die PowerShell-Version, PSScriptAnalyzer-Regeln und Editor-Verhalten fest. The VS Code PowerShell extension provides debugging, IntelliSense, an integrated terminal, and direct Pester test execution. A `settings.json` configuration in `.vscode/` sets the PowerShell version, PSScriptAnalyzer rules, and editor behavior.

JSON – .vscode/settings.json JSON – .vscode/settings.json

    {
      "powershell.powerShellDefaultVersion": "PowerShell (x64)",
      "powershell.scriptAnalysis.enable": true,
      "powershell.scriptAnalysis.settingsPath": "./.vscode/PSScriptAnalyzerSettings.psd1",
      "powershell.pester.outputVerbosity": "Diagnostic",
      "powershell.pester.useLegacyCodeLens": false,
      "editor.formatOnSave": true,
      "[powershell]": {
        "editor.defaultFormatter": "ms-vscode.powershell",
        "editor.tabSize": 4,
        "editor.insertSpaces": true,
        "editor.rulers": [120]
      },
      "files.encoding": "utf8bom"
    }

PowerShell – launch.json für Debugging PowerShell – launch.json for debugging

    // .vscode/launch.json – Debugging-Konfigurationen
    {
      "version": "0.2.0",
      "configurations": [
        {
          "name": "PowerShell: Aktuelles Skript",
          "type": "PowerShell",
          "request": "launch",
          "script": "${file}",
          "args": [],
          "cwd": "${workspaceFolder}"
        },
        {
          "name": "PowerShell: Pester Tests",
          "type": "PowerShell",
          "request": "launch",
          "script": "Invoke-Pester",
          "args": ["-Output", "Diagnostic"],
          "cwd": "${workspaceFolder}"
        },
        {
          "name": "PowerShell: Interaktive Session",
          "type": "PowerShell",
          "request": "launch",
          "script": "",
          "createTemporaryIntegratedConsole": true
        }
      ]
    }

## Profil-Skripte Profile scripts

PowerShell lädt beim Start automatisch Profil-Skripte, die Umgebungsanpassungen, Aliases, Funktionen und Prompt-Konfigurationen enthalten können. Es gibt vier Profile-Ebenen, die in Reihenfolge geladen werden. Die Variable `$PROFILE` zeigt den Pfad des aktuellen Benutzer-/Host-Profils. PowerShell automatically loads profile scripts at startup that can contain environment customizations, aliases, functions, and prompt configurations. There are four profile levels loaded in sequence. The variable `$PROFILE` points to the current user/host profile path.

Profil Profile | Pfad Path | Gilt für Applies to
--- | --- | ---
AllUsersAllHosts AllUsersAllHosts | `$PSHOME\Profile.ps1``$PSHOME\Profile.ps1` | Alle Benutzer, alle Hosts All users, all hosts
AllUsersCurrentHost AllUsersCurrentHost | `$PSHOME\Microsoft.PowerShell_profile.ps1``$PSHOME\Microsoft.PowerShell_profile.ps1` | Alle Benutzer, aktueller Host All users, current host
CurrentUserAllHosts CurrentUserAllHosts | `$HOME\Documents\PowerShell\Profile.ps1``$HOME\Documents\PowerShell\Profile.ps1` | Aktueller Benutzer, alle Hosts Current user, all hosts
CurrentUserCurrentHost CurrentUserCurrentHost | `$PROFILE``$PROFILE` | Aktueller Benutzer, aktueller Host (Standard)Current user, current host (default)

PowerShell PowerShell

    # Profil erstellen falls noch nicht vorhanden
    if (-not (Test-Path $PROFILE)) {
        New-Item -ItemType File -Path $PROFILE -Force
    }

    # Profil öffnen
    code $PROFILE  # VS Code
    notepad $PROFILE  # Notepad

    # Beispielinhalt für $PROFILE (Microsoft.PowerShell_profile.ps1)
    # ------------------------------------------------------------------

    # Aliases
    Set-Alias -Name grep  -Value Select-String
    Set-Alias -Name ll    -Value Get-ChildItem
    Set-Alias -Name touch -Value New-Item

    # Nützliche Funktionen
    function Get-PublicIP { (Invoke-RestMethod https://api.ipify.org?format=json).ip }
    function which ($cmd)  { (Get-Command $cmd).Source }

    # Benutzerdefinierter Prompt mit Git-Branch
    function prompt {
        $branch = git rev-parse --abbrev-ref HEAD 2>$null
        $location = $executionContext.SessionState.Path.CurrentLocation
        if ($branch) {
            Write-Host "[$branch] " -NoNewline -ForegroundColor Cyan
        }
        Write-Host "$location" -NoNewline -ForegroundColor Yellow
        " PS> "
    }

    # PSReadLine konfigurieren (PS7)
    if ($PSVersionTable.PSVersion.Major -ge 7) {
        Set-PSReadLineOption -PredictionSource HistoryAndPlugin
        Set-PSReadLineOption -PredictionViewStyle ListView
        Set-PSReadLineKeyHandler -Key "Ctrl+f" -Function ForwardWord
    }

    # Automatischer Modul-Import
    $commonModules = @("Microsoft.Graph.Authentication", "Az.Accounts")
    foreach ($mod in $commonModules) {
        if (Get-Module -ListAvailable -Name $mod) {
            Import-Module $mod -Verbose:$false
        }
    }

🚫 Profil-Skripte in Produktions-Runbooks 🚫 Profile scripts in production runbooks

Azure Automation Runbooks und GitHub Actions-Jobs laden keine Profil-Skripte. Definiere dort alle benötigten Funktionen und Aliases direkt im Skript oder lade Sie über Module. Verlasse dich nie implizit auf Profil-Konfigurationen in CI/CD-Umgebungen. Azure Automation runbooks and GitHub Actions jobs do not load profile scripts. Define all required functions and aliases directly in the script or load them through modules. Never implicitly rely on profile configurations in CI/CD environments.

## Pipeline-Muster & Leistungsoptimierung Pipeline patterns & performance optimization

Die PowerShell-Pipeline übergibt Objekte zwischen Cmdlets, ohne Strings zu parsen. Effiziente Pipeline-Nutzung, Filterung so früh wie möglich (filter left) und die Vermeidung unnötiger Schleifen sind zentrale Optimierungsprinzipien für skalierbare Skripte. The PowerShell pipeline passes objects between cmdlets without string parsing. Efficient pipeline use, filtering as early as possible (filter left), and avoiding unnecessary loops are key optimization principles for scalable scripts.

PowerShell PowerShell

    # SCHLECHT: Alle Benutzer laden, dann filtern (langsam bei großen Tenants)
    $allUsers = Get-MgUser -All
    $guestUsers = $allUsers | Where-Object UserType -eq "Guest"

    # GUT: Server-seitiger Filter via -Filter (schnell)
    $guestUsers = Get-MgUser -Filter "userType eq 'Guest'" -All `
        -Property DisplayName,UserPrincipalName,CreatedDateTime

    # Measure-Command zum Messen der Laufzeit
    Measure-Command {
        Get-MgUser -Filter "department eq 'Engineering'" -All `
            -Property Id,DisplayName,UserPrincipalName `
        | Select-Object DisplayName,UserPrincipalName `
        | Sort-Object DisplayName `
        | Export-Csv .\engineering_users.csv -NoTypeInformation -Encoding UTF8
    }

    # Parallel-Verarbeitung mit ForEach-Object -Parallel (PS7)
    $userIds = (Get-MgUser -Filter "department eq 'IT'" -All -Property Id).Id
    $results = $userIds | ForEach-Object -Parallel {
        Import-Module Microsoft.Graph.Users
        $groups = (Get-MgUserMemberOf -UserId $_ -Property DisplayName).DisplayName
        [pscustomobject]@{ UserId = $_; GroupCount = $groups.Count }
    } -ThrottleLimit 10

    # StringBuilder statt String-Concatenation für große Strings
    $sb = [System.Text.StringBuilder]::new()
    1..1000 | ForEach-Object { [void]$sb.AppendLine("Row $_") }
    $sb.ToString()