From cdbdb2df3b979688fec462eb18a50b828a614a35 Mon Sep 17 00:00:00 2001 From: Taarly Date: Fri, 12 Dec 2025 10:13:10 +0100 Subject: [PATCH] Test um User im AD anzulegen mit PS Skripts --- app/Services/PowerShellService.php | 96 ++++++++++++++++++++++++ public/ad_create_user.php | 58 ++++++++++++++ public/ad_create_user_form.html | 41 ++++++++++ scripts/powershell/New-AdUserFromPhp.ps1 | 72 ++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 app/Services/PowerShellService.php create mode 100644 public/ad_create_user.php create mode 100644 public/ad_create_user_form.html create mode 100644 scripts/powershell/New-AdUserFromPhp.ps1 diff --git a/app/Services/PowerShellService.php b/app/Services/PowerShellService.php new file mode 100644 index 0000000..7ad83a4 --- /dev/null +++ b/app/Services/PowerShellService.php @@ -0,0 +1,96 @@ +psExe = $psExe ?? 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; + // Alternative PowerShell 7: + // $this->psExe = $psExe ?? 'C:\\Program Files\\PowerShell\\7\\pwsh.exe'; + } + + /** + * @return array{exitCode:int, stdout:string, stderr:string} + */ + public function runScript(string $scriptPath, array $namedArgs, int $timeoutSeconds = 30): array + { + $scriptPath = realpath($scriptPath) ?: $scriptPath; + if (!is_file($scriptPath)) { + throw new RuntimeException("PowerShell script not found: {$scriptPath}"); + } + + $args = []; + foreach ($namedArgs as $name => $value) { + if (!preg_match('/^[A-Za-z][A-Za-z0-9]*$/', (string)$name)) { + throw new InvalidArgumentException("Invalid argument name: {$name}"); + } + $args[] = '-' . $name; + $args[] = (string)$value; // wird als eigenes Argument übergeben (kein Command-String-Basteln) + } + + $cmd = array_merge([ + $this->psExe, + '-NoLogo', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', 'Bypass', + '-File', $scriptPath, + ], $args); + + $descriptors = [ + 0 => ["pipe", "r"], + 1 => ["pipe", "w"], + 2 => ["pipe", "w"], + ]; + + $proc = proc_open($cmd, $descriptors, $pipes, null, null, [ + 'bypass_shell' => true, + 'suppress_errors' => true, + ]); + + if (!is_resource($proc)) { + throw new RuntimeException("Failed to start PowerShell process."); + } + + fclose($pipes[0]); + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + + $stdout = ''; + $stderr = ''; + $start = time(); + + while (true) { + $stdout .= stream_get_contents($pipes[1]); + $stderr .= stream_get_contents($pipes[2]); + + $status = proc_get_status($proc); + if (!$status['running']) { + break; + } + + if ((time() - $start) > $timeoutSeconds) { + proc_terminate($proc, 9); + $stderr .= "\n[PHP] Timeout after {$timeoutSeconds}s"; + break; + } + + usleep(50_000); + } + + fclose($pipes[1]); + fclose($pipes[2]); + + $exitCode = proc_close($proc); + + return [ + 'exitCode' => (int)$exitCode, + 'stdout' => trim($stdout), + 'stderr' => trim($stderr), + ]; + } +} diff --git a/public/ad_create_user.php b/public/ad_create_user.php new file mode 100644 index 0000000..9b0e529 --- /dev/null +++ b/public/ad_create_user.php @@ -0,0 +1,58 @@ + false, 'error' => 'Bitte alle Felder ausfüllen.']); + exit; +} + +try { + $ps = new PowerShellService(); + + $script = __DIR__ . '/../scripts/powershell/New-AdUserFromPhp.ps1'; + + $res = $ps->runScript($script, [ + 'Username' => $username, + 'Vorname' => $vorname, + 'Nachname' => $nachname, + 'Passwort' => $passwort, + 'Benutzergruppe' => $gruppe, + ], 60); + + // PowerShell gibt JSON zurück + $json = $res['stdout']; + $data = json_decode($json, true); + + if (!is_array($data)) { + http_response_code(500); + echo json_encode([ + 'ok' => false, + 'error' => 'Ungültige Antwort von PowerShell.', + 'stderr' => $res['stderr'], + 'raw' => $json, + ]); + exit; + } + + // Wenn PS exitCode != 0, trotzdem JSON ausgeben (enthält error) + if ($res['exitCode'] !== 0) { + http_response_code(400); + } + + echo json_encode($data); +} catch (Throwable $e) { + http_response_code(500); + echo json_encode(['ok' => false, 'error' => $e->getMessage()]); +} diff --git a/public/ad_create_user_form.html b/public/ad_create_user_form.html new file mode 100644 index 0000000..ff4ac86 --- /dev/null +++ b/public/ad_create_user_form.html @@ -0,0 +1,41 @@ + + + + + + AD User anlegen + + +

AD User anlegen

+ +
+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+ + +
+ + diff --git a/scripts/powershell/New-AdUserFromPhp.ps1 b/scripts/powershell/New-AdUserFromPhp.ps1 new file mode 100644 index 0000000..bec6dbc --- /dev/null +++ b/scripts/powershell/New-AdUserFromPhp.ps1 @@ -0,0 +1,72 @@ +param( + [Parameter(Mandatory=$true)] + [ValidatePattern('^[a-zA-Z0-9._-]{1,32}$')] + [string]$Username, + + [Parameter(Mandatory=$true)] + [ValidateLength(1, 64)] + [string]$Vorname, + + [Parameter(Mandatory=$true)] + [ValidateLength(1, 64)] + [string]$Nachname, + + [Parameter(Mandatory=$true)] + [ValidateLength(8, 128)] + [string]$Passwort, + + # Gruppe als Name / DN / SamAccountName der Gruppe + [Parameter(Mandatory=$true)] + [ValidateLength(1, 128)] + [string]$Benutzergruppe +) + +$ErrorActionPreference = "Stop" + +try { + Import-Module ActiveDirectory + + # Existiert User schon? + $existing = Get-ADUser -Filter "SamAccountName -eq '$Username'" -ErrorAction SilentlyContinue + if ($null -ne $existing) { + throw "Benutzer existiert bereits: $Username" + } + + # Existiert Gruppe? + $grp = Get-ADGroup -Identity $Benutzergruppe -ErrorAction SilentlyContinue + if ($null -eq $grp) { + throw "Gruppe nicht gefunden: $Benutzergruppe" + } + + $displayName = "$Vorname $Nachname" + $securePw = ConvertTo-SecureString -AsPlainText $Passwort -Force + + New-ADUser ` + -SamAccountName $Username ` + -Name $displayName ` + -GivenName $Vorname ` + -Surname $Nachname ` + -DisplayName $displayName ` + -AccountPassword $securePw ` + -Enabled $true ` + -ChangePasswordAtLogon $true + + Add-ADGroupMember -Identity $Benutzergruppe -Members $Username + + [pscustomobject]@{ + ok = $true + username = $Username + displayName = $displayName + group = $Benutzergruppe + } | ConvertTo-Json -Depth 4 + + exit 0 +} +catch { + [pscustomobject]@{ + ok = $false + error = $_.Exception.Message + } | ConvertTo-Json -Depth 4 + + exit 1 +}