Wer als Windows-Admin arbeitet und PowerShell noch nicht täglich nutzt, lässt massiv viel Potenzial liegen. Kein Klicken durch GUIs, keine Fehler durch Copy-Paste in Excel – stattdessen saubere, wiederholbare Automatisierung. Dieser Post fasst die Befehle zusammen, die ich im Alltag am häufigsten nutze.


Voraussetzungen

Ich empfehle PowerShell 7 (cross-platform, aktiv weiterentwickelt) statt Windows PowerShell 5.1. Installation auf Windows:

winget install Microsoft.PowerShell

Für die meisten Beispiele hier brauchst du außerdem:

# Active Directory Modul (auf Windows Server vorinstalliert oder via RSAT)
Install-WindowsFeature RSAT-AD-PowerShell

# Microsoft Graph (M365 / Azure)
Install-Module Microsoft.Graph -Scope CurrentUser

# Exchange Online
Install-Module ExchangeOnlineManagement -Scope CurrentUser

Active Directory

Benutzer auflisten und exportieren

Einer meiner meistgenutzten Befehle: alle aktivierten AD-User mit letztem Login exportieren.

Get-ADUser -Filter {Enabled -eq $true} -Properties LastLogonDate, Department, Title |
    Select-Object Name, SamAccountName, Department, Title, LastLogonDate |
    Sort-Object LastLogonDate -Descending |
    Export-Csv "C:\Reports\users_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation -Encoding UTF8

Inaktive Accounts finden

Accounts, die sich 90 Tage nicht eingeloggt haben – ideal für regelmäßige Audits:

$cutoff = (Get-Date).AddDays(-90)

Get-ADUser -Filter {Enabled -eq $true} -Properties LastLogonDate |
    Where-Object { $_.LastLogonDate -lt $cutoff -or $_.LastLogonDate -eq $null } |
    Select-Object Name, SamAccountName, LastLogonDate |
    Export-Csv "C:\Reports\inactive_users.csv" -NoTypeInformation

Benutzer schnell anlegen

Neuer Mitarbeiter, neuer Account. Template-User als Basis:

$template = Get-ADUser "muster.user" -Properties MemberOf, Department, Company
$password = ConvertTo-SecureString "Start@2026!" -AsPlainText -Force

New-ADUser `
    -Name "Max Mustermann" `
    -GivenName "Max" `
    -Surname "Mustermann" `
    -SamAccountName "max.mustermann" `
    -UserPrincipalName "max.mustermann@contoso.de" `
    -Path "OU=Mitarbeiter,DC=contoso,DC=local" `
    -AccountPassword $password `
    -Enabled $true `
    -Department $template.Department `
    -Company $template.Company

# Gruppen vom Template-User kopieren
$template.MemberOf | Add-ADGroupMember -Members "max.mustermann"

Passwort-Ablauf im Blick behalten

Welche User haben ein ablaufendes Passwort in den nächsten 14 Tagen?

$maxAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
$threshold = (Get-Date).AddDays(14)

Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -eq $false} `
    -Properties PasswordLastSet |
    ForEach-Object {
        $expires = $_.PasswordLastSet + $maxAge
        [PSCustomObject]@{
            Name        = $_.Name
            Expires     = $expires
            DaysLeft    = ($expires - (Get-Date)).Days
        }
    } |
    Where-Object { $_.DaysLeft -le 14 -and $_.DaysLeft -ge 0 } |
    Sort-Object DaysLeft

Microsoft 365 via Graph API

Verbinden

Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "Directory.Read.All"

Alle lizenzierten User ausgeben

Get-MgUser -All -Property DisplayName, UserPrincipalName, AssignedLicenses |
    Where-Object { $_.AssignedLicenses.Count -gt 0 } |
    Select-Object DisplayName, UserPrincipalName

UsageLocation für alle User setzen

Ohne UsageLocation lassen sich keine M365-Lizenzen zuweisen – ein klassischer Fallstrick bei Migrationen:

Get-MgUser -All | ForEach-Object {
    if (-not $_.UsageLocation) {
        Update-MgUser -UserId $_.Id -UsageLocation "DE"
        Write-Host "Gesetzt: $($_.DisplayName)"
    }
}

MFA-Status aller User prüfen

Connect-MgGraph -Scopes "UserAuthenticationMethod.Read.All"

Get-MgUser -All | ForEach-Object {
    $methods = Get-MgUserAuthenticationMethod -UserId $_.Id
    [PSCustomObject]@{
        User    = $_.DisplayName
        UPN     = $_.UserPrincipalName
        MFATypes = ($methods.AdditionalProperties.'@odata.type' -join ", ")
    }
} | Export-Csv "C:\Reports\mfa_status.csv" -NoTypeInformation

Exchange Online

Connect-ExchangeOnline -UserPrincipalName admin@contoso.de

Postfachgröße aller Mailboxen

Get-Mailbox -ResultSize Unlimited |
    Get-MailboxStatistics |
    Select-Object DisplayName, TotalItemSize, ItemCount |
    Sort-Object { $_.TotalItemSize.Value.ToBytes() } -Descending |
    Format-Table -AutoSize

Weiterleitungen im Unternehmen aufspüren

Aktive Weiterleitungen sind ein häufiges Sicherheitsrisiko – und oft vergessen:

Get-Mailbox -ResultSize Unlimited |
    Where-Object { $_.ForwardingAddress -or $_.ForwardingSmtpAddress } |
    Select-Object DisplayName, ForwardingAddress, ForwardingSmtpAddress, DeliverToMailboxAndForward

Netzwerk & Server

Port-Erreichbarkeit testen

Ersetzt telnet vollständig – mit mehr Infos:

Test-NetConnection -ComputerName "dc01.contoso.local" -Port 445

# Mehrere Hosts / Ports in einer Schleife
$targets = @("dc01", "dc02", "fileserver01")
$ports   = @(445, 3389, 80, 443)

foreach ($host in $targets) {
    foreach ($port in $ports) {
        $result = Test-NetConnection -ComputerName $host -Port $port -WarningAction SilentlyContinue
        [PSCustomObject]@{
            Host    = $host
            Port    = $port
            Status  = if ($result.TcpTestSucceeded) { "✓ offen" } else { "✗ zu" }
        }
    }
} | Format-Table -AutoSize

DNS-Auflösung auf mehreren Servern testen

$dnsServers = @("8.8.8.8", "1.1.1.1", "192.168.1.1")
$hostname   = "westmeier.cloud"

foreach ($dns in $dnsServers) {
    try {
        $result = Resolve-DnsName -Name $hostname -Server $dns -ErrorAction Stop
        Write-Host "$dns$($result[0].IPAddress)" -ForegroundColor Green
    } catch {
        Write-Host "$dns → Fehler: $_" -ForegroundColor Red
    }
}

Freien Festplattenplatz überwachen

Get-PSDrive -PSProvider FileSystem |
    Where-Object { $_.Used -gt 0 } |
    Select-Object Name,
        @{N="Gesamt (GB)"; E={[math]::Round($_.Used / 1GB + $_.Free / 1GB, 1)}},
        @{N="Frei (GB)";   E={[math]::Round($_.Free / 1GB, 1)}},
        @{N="Frei (%)";    E={[math]::Round(($_.Free / ($_.Used + $_.Free)) * 100, 1)}} |
    Format-Table -AutoSize

Fehlerbehandlung & Logging

Produktions-Skripte brauchen ordentliches Logging. Mein Standard-Template:

$LogFile = "C:\Logs\script_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $line = "[$timestamp] [$Level] $Message"
    Add-Content -Path $LogFile -Value $line
    switch ($Level) {
        "ERROR" { Write-Host $line -ForegroundColor Red }
        "WARN"  { Write-Host $line -ForegroundColor Yellow }
        default { Write-Host $line -ForegroundColor Cyan }
    }
}

try {
    Write-Log "Skript gestartet"
    # ... deine Logik ...
    Write-Log "Skript erfolgreich abgeschlossen"
} catch {
    Write-Log "Kritischer Fehler: $_" -Level "ERROR"
    exit 1
}

Nützliche One-Liner

Kleine Helfer, die ich immer wieder brauche:

# Laufende Dienste mit hohem RAM-Verbrauch
Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 10 Name, @{N="RAM (MB)"; E={[math]::Round($_.WorkingSet64/1MB,1)}}

# Alle lokalen Admins auflisten
Get-LocalGroupMember -Group "Administrators"

# Zertifikate, die in 30 Tagen ablaufen
Get-ChildItem Cert:\LocalMachine\My |
    Where-Object { $_.NotAfter -lt (Get-Date).AddDays(30) } |
    Select-Object Subject, NotAfter

# Letzten Reboot ermitteln
(Get-CimInstance Win32_OperatingSystem).LastBootUpTime

# Aktuell eingeloggte User auf Remote-System
query user /server:SERVERNAME

# Datei-Hash prüfen (z.B. nach Download)
Get-FileHash "C:\Downloads\setup.exe" -Algorithm SHA256

Fazit

PowerShell ist kein “Nice to have” — es ist das Werkzeug, das dich von einem reaktiven zu einem proaktiven Admin macht. Einmal geschriebene Skripte laufen zuverlässig, dokumentieren sich selbst durch Write-Log, und ersparen dir Stunden manueller Arbeit pro Woche.

Mein Tipp für den Einstieg: Nimm eine Aufgabe, die du regelmäßig manuell machst – Benutzerexport, Port-Check, Speicherübersicht – und automatisiere genau diese eine Sache. Der Rest folgt von selbst.