Mehrere AD-Benutzer automatisch per PowerShell aus CSV anlegen

Active Directory: Mehrere Benutzer automatisch per PowerShell aus CSV anlegen (inkl. Umlaute, Dubletten, Logging)
Wenn du regelmäßig neue Benutzer in AD anlegen musst (1 oder 100), ist PowerShell + CSV die sauberste Lösung. Du sparst Zeit, vermeidest Tippfehler und bekommst gleich ein Log, was wirklich passiert ist.
In diesem Artikel zeige ich eine praxistaugliche „Admin-Version“ mit:
- CSV-Import (GivenName, Surname)
- automatischem Login nach Schema vorname.nachname
- Umlaut-Umschreibung (Jörg Weiß → joerg.weiss)
- Dubletten-Check (wenn es den Login schon gibt → noah.fischer2, noah.fischer3 …)
- zufälligem Startpasswort
- optionaler verschlüsselter Passwort-Export (nicht im Klartext)
- Log-Datei für Nachvollziehbarkeit
Voraussetzungen
- Du führst das Script auf einem PC/Server aus, der die RSAT AD Tools hat oder direkt auf dem DC.
- PowerShell als Administrator starten.
- Du hast Rechte, Benutzer in der Ziel-OU anzulegen.
1) CSV vorbereiten
Erstelle users.csv in z.B. C:\Users\bauer.AD\Documents
Inhalt:
GivenName,Surname
Noah,Fischer
Jörg,Weiß
Mia,Weber
Wichtig:
- Spalten heißen exakt GivenName und Surname
- Standard ist Komma als Trennzeichen. Wenn Excel bei dir Semikolon nutzt, stellen wir das im Script um.
2) Ziel-OU prüfen
Die Benutzer sollen bei dir nach:
rubinhood → Users
Also:
OU=Users,OU=rubinhood,DC=ad,DC=rubinhood,DC=de
Wenn du sicher sein willst:
Get-ADOrganizationalUnit -Filter 'Name -eq "Users"' | Select Name, DistinguishedName
3) PowerShell Script (Profi-Version)
✅ Dieses Script ist bewusst so gebaut, dass es auch in der Konsole funktioniert (ohne $PSScriptRoot), weil Leute gern alles in C:\Windows\System32 ausführen und sich dann wundern.
Du musst nur
$BaseFolderanpassen (wousers.csvliegt).
Import-Module ActiveDirectory
# ========== Konfiguration ==========
$OU = "OU=Users,OU=rubinhood,DC=ad,DC=rubinhood,DC=de"
$UpnSuffix = "ad.rubinhood.de"
# Ordner, wo users.csv liegt (anpassen!)
$BaseFolder = "C:\Users\bauer.AD\Documents"
$CsvPath = Join-Path $BaseFolder "users.csv"
$CsvDelimiter = "," # falls Excel Semikolon: ";"
# Log-Dateien (mit Timestamp)
$Stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$LogFile = Join-Path $BaseFolder "ADUserCreate_$Stamp.log"
# Optional: verschlüsselte Passwortliste (nur auf diesem Windows-User entschlüsselbar)
$EnableEncryptedPasswordExport = $true
$EncryptedPwFile = Join-Path $BaseFolder "InitialPasswords_$Stamp.clixml"
# ========== Hilfsfunktionen ==========
function Write-Log {
param(
[string]$Message,
[ValidateSet("INFO","WARN","ERROR")] [string]$Level = "INFO"
)
$line = "{0} [{1}] {2}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Level, $Message
Add-Content -Path $LogFile -Value $line -Encoding UTF8
}
function Normalize-LoginPart {
param([string]$s)
if ([string]::IsNullOrWhiteSpace($s)) { return "" }
$s = $s.Trim().ToLower()
# Umlaute/ß ersetzen
$s = $s.Replace("ä","ae").Replace("ö","oe").Replace("ü","ue").Replace("ß","ss")
# Sonderzeichen entfernen (erlaubt: a-z, 0-9, punkt)
$s = $s -replace "[^a-z0-9\.]", ""
# Mehrere Punkte zusammenfassen, Punkt am Anfang/Ende entfernen
$s = $s -replace "\.+", "."
$s = $s.Trim(".")
return $s
}
function New-RandomPassword {
param([int]$Length = 14)
if ($Length -lt 10) { $Length = 10 }
$upper = "ABCDEFGHJKLMNPQRSTUVWXYZ"
$lower = "abcdefghijkmnopqrstuvwxyz"
$digits = "23456789"
$special = "!@#$%*?-_"
$chars = @()
$chars += ($upper.ToCharArray() | Get-Random -Count 1)
$chars += ($lower.ToCharArray() | Get-Random -Count 1)
$chars += ($digits.ToCharArray() | Get-Random -Count 1)
$chars += ($special.ToCharArray() | Get-Random -Count 1)
$all = ($upper + $lower + $digits + $special).ToCharArray()
for ($i=1; $i -le ($Length - 4); $i++) {
$chars += ($all | Get-Random)
}
return (-join ($chars | Get-Random -Count $chars.Count))
}
function Get-UniqueSamAccountName {
param([string]$BaseSam)
if ([string]::IsNullOrWhiteSpace($BaseSam)) { return $null }
# SamAccountName praktisch max 20 Zeichen
if ($BaseSam.Length -gt 20) { $BaseSam = $BaseSam.Substring(0,20) }
$sam = $BaseSam
$i = 2
while (Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue) {
$suffix = "$i"
$cut = [Math]::Min(20 - $suffix.Length, $BaseSam.Length)
$sam = $BaseSam.Substring(0, $cut) + $suffix
$i++
}
return $sam
}
# ========== Start ==========
Write-Log "Start: AD User Creation Script"
Write-Log "CSV: $CsvPath"
Write-Log "OU : $OU"
if (-not (Test-Path $CsvPath)) {
Write-Log "CSV nicht gefunden: $CsvPath" "ERROR"
throw "CSV nicht gefunden: $CsvPath"
}
$PasswordExport = @()
Import-Csv -Path $CsvPath -Delimiter $CsvDelimiter | ForEach-Object {
try {
$Given = $_.GivenName
$Sur = $_.Surname
if ([string]::IsNullOrWhiteSpace($Given) -or [string]::IsNullOrWhiteSpace($Sur)) {
Write-Log "Übersprungen: GivenName oder Surname fehlt in einer CSV-Zeile." "WARN"
return
}
# Login: vorname.nachname (mit Umlaut-Umschreibung)
$baseSam = (Normalize-LoginPart $Given) + "." + (Normalize-LoginPart $Sur)
$baseSam = $baseSam.Trim(".")
if ([string]::IsNullOrWhiteSpace($baseSam)) {
Write-Log "Übersprungen: Login konnte nicht generiert werden für '$Given $Sur'." "WARN"
return
}
# Dubletten lösen
$Sam = Get-UniqueSamAccountName -BaseSam $baseSam
$Upn = "$Sam@$UpnSuffix"
# Zufälliges Startpasswort
$PlainPw = New-RandomPassword -Length 14
$PwSecure = ConvertTo-SecureString $PlainPw -AsPlainText -Force
# Benutzer anlegen
New-ADUser -Name "$Given $Sur" `
-GivenName $Given `
-Surname $Sur `
-DisplayName "$Given $Sur" `
-SamAccountName $Sam `
-UserPrincipalName $Upn `
-Path $OU `
-AccountPassword $PwSecure `
-Enabled $true `
-ChangePasswordAtLogon $true
Write-Log "Erstellt: '$Given $Sur' | SAM='$Sam' | UPN='$Upn'"
# Optional: Passwortliste verschlüsselt speichern
if ($EnableEncryptedPasswordExport) {
$PasswordExport += [pscustomobject]@{
SamAccountName = $Sam
UserPrincipalName = $Upn
GivenName = $Given
Surname = $Sur
InitialPassword = (ConvertTo-SecureString $PlainPw -AsPlainText -Force)
}
}
} catch {
Write-Log "Fehler bei '$($_.GivenName) $($_.Surname)': $($_.Exception.Message)" "ERROR"
}
}
if ($EnableEncryptedPasswordExport -and $PasswordExport.Count -gt 0) {
$PasswordExport | Export-Clixml -Path $EncryptedPwFile
Write-Log "Verschlüsselte Passwortliste gespeichert: $EncryptedPwFile"
}
Write-Log "Ende: Script fertig"
Write-Host "Fertig."
Write-Host "Log: $LogFile"
if ($EnableEncryptedPasswordExport) { Write-Host "Passwortliste (verschlüsselt): $EncryptedPwFile" }
4) Script ausführen
PowerShell als Admin öffnen:
cd C:\Users\bauer.AD\Documents
.\Create-ADUsers.ps1
(oder Script rein kopieren und laufen lassen, wenn du gern gefährlich lebst)
Was du danach bekommst
- Benutzer sind in
rubinhood\Userssichtbar (ADUC /dsa.msc) - Log-Datei wie:
ADUserCreate_20260114_211026.log - optional:
InitialPasswords_*.clixml(verschlüsselt, kein Klartext)

Typische Fehler (und schnelle Lösungen)
CSV nicht gefunden
- Pfad stimmt nicht oder du bist im falschen Ordner
→
$BaseFolderprüfen
Passwort-Policy
- Wenn Domain Policy strenger ist, kann selbst “zufällig” zu schwach sein → Passwortregeln anpassen oder Generator erweitern
Umlaute / Sonderzeichen
- Werden automatisch ersetzt und bereinigt → Login bleibt AD-tauglich
Doppelte Namen
- Script vergibt automatisch
noah.fischer2,noah.fischer3usw. → Standard in vielen Umgebungen, weil eindeutig und simpel