Test um User im AD anzulegen mit PS Skripts
This commit is contained in:
parent
1a5875816c
commit
cdbdb2df3b
96
app/Services/PowerShellService.php
Normal file
96
app/Services/PowerShellService.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
final class PowerShellService
|
||||||
|
{
|
||||||
|
private string $psExe;
|
||||||
|
|
||||||
|
public function __construct(?string $psExe = null)
|
||||||
|
{
|
||||||
|
// Windows PowerShell 5.1
|
||||||
|
$this->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),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
58
public/ad_create_user.php
Normal file
58
public/ad_create_user.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../app/Services/PowerShellService.php';
|
||||||
|
|
||||||
|
// Minimal-Validation (zusätzlich zur PS-Validation)
|
||||||
|
$username = (string)($_POST['username'] ?? '');
|
||||||
|
$vorname = (string)($_POST['vorname'] ?? '');
|
||||||
|
$nachname = (string)($_POST['nachname'] ?? '');
|
||||||
|
$passwort = (string)($_POST['passwort'] ?? '');
|
||||||
|
$gruppe = (string)($_POST['gruppe'] ?? '');
|
||||||
|
|
||||||
|
if ($username === '' || $vorname === '' || $nachname === '' || $passwort === '' || $gruppe === '') {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['ok' => 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()]);
|
||||||
|
}
|
||||||
41
public/ad_create_user_form.html
Normal file
41
public/ad_create_user_form.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>AD User anlegen</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>AD User anlegen</h1>
|
||||||
|
|
||||||
|
<form method="post" action="ad_create_user.php">
|
||||||
|
<p>
|
||||||
|
<label>Username (sAMAccountName)<br>
|
||||||
|
<input name="username" required maxlength="32">
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Vorname<br>
|
||||||
|
<input name="vorname" required maxlength="64">
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Nachname<br>
|
||||||
|
<input name="nachname" required maxlength="64">
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Passwort<br>
|
||||||
|
<input name="passwort" type="password" required minlength="8" maxlength="128">
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Benutzergruppe (Gruppenname / DN)<br>
|
||||||
|
<input name="gruppe" required maxlength="128" placeholder="z.B. 'IT-Users'">
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button type="submit">Anlegen</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
72
scripts/powershell/New-AdUserFromPhp.ps1
Normal file
72
scripts/powershell/New-AdUserFromPhp.ps1
Normal file
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user