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.
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.
5.1 vs. 7.x, .NET-Basis, Support-Matrix 5.1 vs. 7.x, .NET base, support matrix
Windows, Linux, macOS, Container Windows, Linux, macOS, containers
Variablen, Typen, Arrays, Hashtables Variables, types, arrays, hashtables
if, switch, for, foreach, while if, switch, for, foreach, while
Advanced Functions, CmdletBinding, Parameter Advanced functions, CmdletBinding, parameters
Erstellen, Manifest, PSGallery Create, manifest, PSGallery
PS-Klassen, Vererbung, Interfaces PS classes, inheritance, interfaces
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 |
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 |
# 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 |
# 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.
# 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)] |
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.
# 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.
# 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 |
# 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).
# 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.
# 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.
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
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 |
# 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.
{
"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"
}
// .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) |
# 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
}
}
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.
# 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()