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.

VersionVersion .NET-Basis.NET base PlattformPlatform StatusStatus BesonderheitenHighlights
5.15.1 .NET Framework 4.8.NET Framework 4.8 Windows onlyWindows only WartungsmodusMaintenance mode WMI, COM, vollständige Module-KompatibilitätWMI, COM, full module compatibility
7.2 (LTS)7.2 (LTS) .NET 6.NET 6 Win / Linux / macOSWin / Linux / macOS LTS bis Nov 2024LTS until Nov 2024 Stabil, breite Enterprise-AdoptionStable, broad enterprise adoption
7.4 (LTS)7.4 (LTS) .NET 8.NET 8 Win / Linux / macOSWin / Linux / macOS LTS aktivLTS active Empfohlen, ForEach-Object -Parallel, null-KoaleszenzRecommended, ForEach-Object -Parallel, null coalescing
7.57.5 .NET 9.NET 9 Win / Linux / macOSWin / Linux / macOS AktuellCurrent Neueste Features, kürzerer Support-ZyklusLatest 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.

MethodeMethod BefehlCommand PlattformPlatform
wingetwinget winget install --id Microsoft.PowerShellwinget install --id Microsoft.PowerShell WindowsWindows
MSI-PaketMSI package GitHub Releases herunterladenDownload from GitHub Releases WindowsWindows
HomebrewHomebrew brew install --cask powershellbrew install --cask powershell macOSmacOS
apt (Ubuntu)apt (Ubuntu) apt-get install -y powershellapt-get install -y powershell LinuxLinux
DockerDocker docker run -it mcr.microsoft.com/powershelldocker run -it mcr.microsoft.com/powershell ContainerContainer
GitHub ActionsGitHub Actions pwsh vorinstalliert auf ubuntu-latestpwsh pre-installed on ubuntu-latest CI/CDCI/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.

KonstruktConstruct BeispielExample HinweisNote
VariableVariable $name = "Alice"$name = "Alice" Automatische TyperkennungAutomatic type inference
Typisierte VariableTyped variable [int]$count = 42[int]$count = 42 Casting wird erzwungenCasting is enforced
ArrayArray $arr = @(1, 2, 3)$arr = @(1, 2, 3) Nullbasierter Index, .CountZero-based index, .Count
HashtableHashtable $h = @{Key = "Value"}$h = @{Key = "Value"} Key-Value-Zugriff via $h.KeyKey-value access via $h.Key
Ordered HashtableOrdered hashtable [ordered]@{A=1; B=2}[ordered]@{A=1; B=2} Einfügereihenfolge erhaltenInsertion order preserved
Here-StringHere-string @"..."@@"..."@ Mehrzeiliger String mit InterpolationMulti-line string with interpolation
Null-Koaleszenz (PS7)Null coalescing (PS7) $x ?? "default"$x ?? "default" Fallback wenn $nullFallback when $null
Ternary (PS7)Ternary (PS7) $x ? "yes" : "no"$x ? "yes" : "no" Kurzform für if/elseShorthand 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.

AttributAttribute ZweckPurpose BeispielExample
[CmdletBinding()][CmdletBinding()] Aktiviert Cmdlet-Verhalten: Verbose, Debug, WhatIfEnables cmdlet behavior: Verbose, Debug, WhatIf Immer für öffentliche Funktionen verwendenAlways use for public functions
[Parameter(Mandatory)][Parameter(Mandatory)] Pflichtparameter, Benutzer wird interaktiv gefragtRequired parameter, user is prompted interactively [Parameter(Mandatory)][Parameter(Mandatory)]
[Parameter(ValueFromPipeline)][Parameter(ValueFromPipeline)] Erlaubt Pipeline-InputAllows pipeline input [Parameter(ValueFromPipeline=$true)][Parameter(ValueFromPipeline=$true)]
[ValidateSet()][ValidateSet()] Einschränkung auf erlaubte WerteRestrict to allowed values [ValidateSet("Dev","Test","Prod")][ValidateSet("Dev","Test","Prod")]
[ValidatePattern()][ValidatePattern()] Regex-Validierung des EingabewertsRegex validation of input value [ValidatePattern("^[A-Z]{3}\d{4}$")][ValidatePattern("^[A-Z]{3}\d{4}$")]
[OutputType()][OutputType()] Dokumentiert den RückgabetypDocuments the return type [OutputType([pscustomobject])][OutputType([pscustomobject])]
SupportsShouldProcessSupportsShouldProcess Aktiviert -WhatIf und -ConfirmEnables -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.

CmdletCmdlet ZweckPurpose HinweisNote
Enable-PSRemotingEnable-PSRemoting WinRM aktivieren und konfigurierenEnable and configure WinRM Als Administrator auf ZielcomputerRun as administrator on target
New-PSSessionNew-PSSession Persistente Remote-Verbindung aufbauenEstablish persistent remote connection Objekte und Variablen bleiben erhaltenObjects and variables persist
Invoke-CommandInvoke-Command Befehl auf Remote-Computer ausführenRun command on remote computer Gleichzeitig auf mehrere ZieleTarget multiple computers simultaneously
Enter-PSSessionEnter-PSSession Interaktive Remote-ShellInteractive remote shell Für interaktive DiagnoseFor interactive diagnostics
Copy-Item -ToSessionCopy-Item -ToSession Datei zu Remote-Session kopierenCopy file to remote session Kein Netzwerkfreigabe erforderlichNo 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.

BereichArea EmpfehlungRecommendation BegründungRationale
BenennungNaming Verb-Noun mit genehmigten Verben (Get-Verb)Verb-Noun with approved verbs (Get-Verb) Konsistenz mit dem PS-ÖkosystemConsistency with the PS ecosystem
SecretsSecrets Nie Passwörter im Code; SecretManagement oder Key Vault verwendenNever hardcode passwords; use SecretManagement or Key Vault Sicherheit und AuditierbarkeitSecurity and auditability
TypenTypes Parameter-Typen immer angebenAlways specify parameter types Frühe Fehlererkennung, besseres IntelliSenseEarly error detection, better IntelliSense
LintingLinting Invoke-ScriptAnalyzer im CI integrierenIntegrate Invoke-ScriptAnalyzer in CI Einheitliche Codequalität im TeamConsistent code quality across the team
TestenTesting Pester 5 für Unit- und Integration-TestsPester 5 for unit and integration tests Regression- und CI/CD-GatesRegression and CI/CD gates
Pipeline-OutputPipeline output Objekte ausgeben, nie Strings parsenOutput objects, never parse strings Downstream-Kompatibilität und FilterungDownstream compatibility and filtering
Module versionenModule versions -RequiredVersion in CI-Umgebungen-RequiredVersion in CI environments Reproduzierbare BuildsReproducible builds
ParallelitätParallelism ForEach-Object -Parallel mit -ThrottleLimitForEach-Object -Parallel with -ThrottleLimit Performance bei Massen-OperationenPerformance 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.

FeatureFeature PowerShell ISEPowerShell ISE VS Code + PS ExtensionVS Code + PS Extension
PS-VersionPS version 5.1 only5.1 only 5.1 + 7.x5.1 + 7.x
PlattformPlatform Windows onlyWindows only Win / Linux / macOSWin / Linux / macOS
IntelliSenseIntelliSense GrundlegendBasic Vollständig, inkl. ParameterinfoFull, including parameter info
DebuggingDebugging Breakpoints, WatchBreakpoints, Watch Breakpoints, Watch, Conditional, LogpointsBreakpoints, Watch, Conditional, Logpoints
Git-IntegrationGit integration KeineNone Vollständig integriertFully integrated
ExtensionsExtensions KeineNone Marketplace mit tausenden ExtensionsMarketplace with thousands of extensions
Remote DevelopmentRemote development NeinNo SSH, WSL, Container, CodespacesSSH, WSL, Container, Codespaces
Pester-IntegrationPester integration ManuellManual Test Explorer ExtensionTest Explorer extension
WartungsstatusMaintenance status Eingestellt (keine neuen Features)Discontinued (no new features) Aktiv weiterentwickeltActively 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.

ProfilProfile PfadPath Gilt fürApplies to
AllUsersAllHostsAllUsersAllHosts $PSHOME\Profile.ps1$PSHOME\Profile.ps1 Alle Benutzer, alle HostsAll users, all hosts
AllUsersCurrentHostAllUsersCurrentHost $PSHOME\Microsoft.PowerShell_profile.ps1$PSHOME\Microsoft.PowerShell_profile.ps1 Alle Benutzer, aktueller HostAll users, current host
CurrentUserAllHostsCurrentUserAllHosts $HOME\Documents\PowerShell\Profile.ps1$HOME\Documents\PowerShell\Profile.ps1 Aktueller Benutzer, alle HostsCurrent user, all hosts
CurrentUserCurrentHostCurrentUserCurrentHost $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()