PHP_AdminTool_Projekt/scripts/powershell/create_users_csv.ps1

177 lines
6.3 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

param(
[Parameter(Mandatory=$true)]
[string]$InputFile
)
# Read meta JSON
try {
$json = Get-Content -Raw -Path $InputFile -ErrorAction Stop
$meta = $json | ConvertFrom-Json
} catch {
Write-Output (@{ success = $false; message = "Failed to read/parse meta JSON: $($_.Exception.Message)" } | ConvertTo-Json -Compress)
exit 1
}
$csvFile = [string]$meta.input_file
# PowerShell 5.1 doesn't support the null-coalescing operator '??'.
# Use an explicit check here to set the default delimiter.
$delimiter = [string]$meta.delimiter
if ([string]::IsNullOrWhiteSpace($delimiter)) { $delimiter = ',' }
$hasHeader = [bool]($meta.has_header -as [bool])
$dryRun = [bool]($meta.dry_run -as [bool])
$defaultOu = [string]$meta.default_ou
if (-not (Test-Path -Path $csvFile)) {
Write-Output (@{ success = $false; message = "CSV file not found: $csvFile" } | ConvertTo-Json -Compress)
exit 1
}
# Ensure ActiveDirectory module is available
try {
Import-Module ActiveDirectory -ErrorAction Stop
} catch {
Write-Output (@{ success = $false; message = "ActiveDirectory PowerShell module not available: $($_.Exception.Message)" } | ConvertTo-Json -Compress)
exit 1
}
# Read CSV
try {
if ($hasHeader) {
$items = Import-Csv -Path $csvFile -Delimiter $delimiter -ErrorAction Stop
} else {
# Use default headers
$headers = 'samaccountname','displayname','mail','password','groups'
$items = Import-Csv -Path $csvFile -Delimiter $delimiter -Header $headers -ErrorAction Stop
}
} catch {
Write-Output (@{ success = $false; message = "Failed to parse CSV: $($_.Exception.Message)" } | ConvertTo-Json -Compress)
exit 1
}
$actor = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$results = @()
$successCount = 0
$failCount = 0
foreach ($row in $items) {
$sam = $row.samaccountname
$display = $row.displayname
$mail = $row.mail
$pass = $row.password
$groups = $row.groups
if ([string]::IsNullOrWhiteSpace($ou) -and -not [string]::IsNullOrWhiteSpace($defaultOu)) {
$ou = $defaultOu
}
# Password hint text (German)
$passwordHint = @"
Das sollten die Anforderungen an das Passwort sein:
- mindestens 7 Zeichen
- darf den Benutzer-/Accountnamen nicht enthalten (bzw. keine zu großen Teile davon)
- muss Zeichen aus mindestens 3 von 4 Kategorien enthalten:
1) Großbuchstaben (AZ)
2) Kleinbuchstaben (az)
3) Ziffern (09)
4) Sonderzeichen (alles, was kein Buchstabe/Zahl ist, z. B. ! ? # _ - . , usw.)
"@
function Test-PasswordRequirements {
param(
[string]$Password,
[string]$SamAccountName
)
$errors = @()
if ([string]::IsNullOrEmpty($Password) -or $Password.Length -lt 7) {
$errors += 'Passwort muss mindestens 7 Zeichen lang sein.'
}
# Categories: uppercase, lowercase, digit, special
$categories = 0
if ($Password -match '[A-Z]') { $categories++ }
if ($Password -match '[a-z]') { $categories++ }
if ($Password -match '\d') { $categories++ }
if ($Password -match '[^A-Za-z0-9]') { $categories++ }
if ($categories -lt 3) {
$errors += 'Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten (Groß, Klein, Ziffern, Sonderzeichen).'
}
# Check for username inclusion or large parts of it (case-insensitive)
if (-not [string]::IsNullOrEmpty($SamAccountName)) {
$pwLower = $Password.ToLowerInvariant()
$samLower = $SamAccountName.ToLowerInvariant()
if ($pwLower -like "*${samLower}*") {
$errors += 'Passwort darf den Benutzernamen nicht enthalten.'
} else {
# Check for substrings of username of length >= 4
$minLen = 4
if ($samLower.Length -ge $minLen) {
for ($len = $minLen; $len -le $samLower.Length; $len++) {
for ($start = 0; $start -le $samLower.Length - $len; $start++) {
$sub = $samLower.Substring($start, $len)
if ($pwLower -like "*${sub}*") {
$errors += 'Passwort darf keine größeren Teile des Benutzernamens enthalten.'
break 2
}
}
}
}
}
}
return $errors
}
if ([string]::IsNullOrWhiteSpace($sam) -or [string]::IsNullOrWhiteSpace($pass)) {
$results += @{ sam = $sam; success = $false; message = 'Missing samaccountname or password' }
$failCount++
continue
}
# Validate password according to requirements, also during dry run so issues are visible early
$pwErrors = Test-PasswordRequirements -Password $pass -SamAccountName $sam
if ($pwErrors.Count -gt 0) {
$results += @{ sam = $sam; success = $false; message = ('Invalid password: ' + ($pwErrors -join ' | ')); hint = $passwordHint }
$failCount++
continue
}
if ($dryRun) {
$results += @{ sam = $sam; success = $true; message = 'DRY RUN: would create (password validated)' }
$successCount++
continue
}
try {
$props = @{ Name = if ($display -and $display -ne '') { $display } else { $sam }; SamAccountName = $sam; Enabled = $true }
if ($mail -and $mail -ne '') { $props['EmailAddress'] = $mail }
if ($ou -and $ou -ne '') { $props['Path'] = $ou }
$securePass = ConvertTo-SecureString $pass -AsPlainText -Force
$props['AccountPassword'] = $securePass
New-ADUser @props -ErrorAction Stop
if ($groups -and $groups -ne '') {
$groupList = $groups -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
foreach ($g in $groupList) {
Add-ADGroupMember -Identity $g -Members $sam -ErrorAction Stop
}
}
$results += @{ sam = $sam; success = $true; message = 'Created' }
$successCount++
} catch {
$results += @{ sam = $sam; success = $false; message = $_.Exception.Message }
$failCount++
}
}
$output = @{ success = $failCount -eq 0; message = "Created $successCount users, $failCount failures"; details = $results; actor = $actor }
Write-Output ($output | ConvertTo-Json -Compress)
exit 0