export angepasst
This commit is contained in:
@@ -9,54 +9,60 @@ param (
|
|||||||
[string]$LogPath = (Join-Path $PSScriptRoot ("export_log_" + (Get-Date -Format 'yyyyMMdd_HHmmss') + ".txt"))
|
[string]$LogPath = (Join-Path $PSScriptRoot ("export_log_" + (Get-Date -Format 'yyyyMMdd_HHmmss') + ".txt"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Ensure ActiveDirectory module is available
|
# Ensure AD module is loaded — called lazily before first AD operation
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
function Initialize-ADModule {
|
||||||
|
if (Get-Module -Name ActiveDirectory) { return $true }
|
||||||
|
|
||||||
if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
|
if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
|
||||||
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
|
||||||
|
[Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
if (-not $isAdmin) {
|
if (-not $isAdmin) {
|
||||||
Write-Error "ActiveDirectory module not found. Please run this script as Administrator to install it automatically, or install RSAT manually."
|
[System.Windows.Forms.MessageBox]::Show(
|
||||||
exit 1
|
"Das ActiveDirectory-Modul wurde nicht gefunden.`nBitte das Skript als Administrator starten.",
|
||||||
|
"Modul fehlt", "OK", "Error") | Out-Null
|
||||||
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "ActiveDirectory module not found. Attempting to install..." -ForegroundColor Yellow
|
|
||||||
$os = (Get-CimInstance Win32_OperatingSystem).Caption
|
|
||||||
|
|
||||||
if ($os -match "Server") {
|
|
||||||
try {
|
try {
|
||||||
|
$os = (Get-CimInstance Win32_OperatingSystem).Caption
|
||||||
|
if ($os -match "Server") {
|
||||||
Import-Module ServerManager -ErrorAction Stop
|
Import-Module ServerManager -ErrorAction Stop
|
||||||
Add-WindowsFeature RSAT-AD-PowerShell -ErrorAction Stop | Out-Null
|
Add-WindowsFeature RSAT-AD-PowerShell -ErrorAction Stop | Out-Null
|
||||||
Write-Host "ActiveDirectory module installed via Add-WindowsFeature." -ForegroundColor Green
|
|
||||||
} catch {
|
|
||||||
Write-Error "Failed to install ActiveDirectory module on Server: $_"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
|
||||||
$feature = Get-WindowsCapability -Name "Rsat.ActiveDirectory*" -Online -ErrorAction Stop |
|
$feature = Get-WindowsCapability -Name "Rsat.ActiveDirectory*" -Online -ErrorAction Stop |
|
||||||
Where-Object State -ne Installed | Select-Object -First 1
|
Where-Object State -ne Installed | Select-Object -First 1
|
||||||
if ($feature) {
|
if ($feature) {
|
||||||
Add-WindowsCapability -Name $feature.Name -Online -ErrorAction Stop | Out-Null
|
Add-WindowsCapability -Name $feature.Name -Online -ErrorAction Stop | Out-Null
|
||||||
Write-Host "ActiveDirectory module installed via Add-WindowsCapability." -ForegroundColor Green
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "Failed to install ActiveDirectory module on Windows client: $_"
|
[System.Windows.Forms.MessageBox]::Show(
|
||||||
exit 1
|
"RSAT konnte nicht installiert werden:`n$_", "Fehler", "OK", "Error") | Out-Null
|
||||||
}
|
return $false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Import-Module ActiveDirectory -ErrorAction Stop
|
Import-Module ActiveDirectory -ErrorAction Stop -WarningAction SilentlyContinue
|
||||||
|
return $true
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "ActiveDirectory module could not be loaded: $_"
|
[System.Windows.Forms.MessageBox]::Show(
|
||||||
exit 1
|
"ActiveDirectory-Modul konnte nicht geladen werden:`n$_", "Fehler", "OK", "Error") | Out-Null
|
||||||
|
return $false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Add-Type -AssemblyName System.Windows.Forms
|
# ---------------------------------------------------------------------------
|
||||||
Add-Type -AssemblyName System.Drawing
|
# Script-scope state for the recursive export
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
[System.Windows.Forms.Application]::EnableVisualStyles()
|
$script:visitedGroups = $null
|
||||||
|
$script:exportResults = $null
|
||||||
|
$script:exportAdParams = $null
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
@@ -84,35 +90,13 @@ function Write-OutputLine {
|
|||||||
[System.Windows.Forms.Application]::DoEvents()
|
[System.Windows.Forms.Application]::DoEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
function Start-Export {
|
|
||||||
param([string]$GroupName, [string]$OutputPath, [string]$Server)
|
|
||||||
|
|
||||||
$script:btnExport.Enabled = $false
|
|
||||||
$script:rtbOutput.Clear()
|
|
||||||
|
|
||||||
$adParams = @{}
|
|
||||||
if ($Server) { $adParams['Server'] = $Server }
|
|
||||||
|
|
||||||
# Validate group
|
|
||||||
try {
|
|
||||||
$null = Get-ADGroup -Identity $GroupName -ErrorAction Stop @adParams
|
|
||||||
Write-Log "Gruppe gefunden: $GroupName"
|
|
||||||
} catch {
|
|
||||||
Write-Log "Gruppe '$GroupName' nicht gefunden: $_" -Level ERROR
|
|
||||||
$script:btnExport.Enabled = $true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$visitedGroups = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
|
|
||||||
$results = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
||||||
|
|
||||||
function Get-NestedMembers {
|
function Get-NestedMembers {
|
||||||
param([string]$Group, [string]$SourceGroup)
|
param([string]$Group, [string]$SourceGroup)
|
||||||
|
|
||||||
if (-not $visitedGroups.Add($Group)) { return }
|
if (-not $script:visitedGroups.Add($Group)) { return }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$members = Get-ADGroupMember -Identity $Group -ErrorAction Stop @adParams
|
$members = Get-ADGroupMember -Identity $Group -ErrorAction Stop @script:exportAdParams
|
||||||
} catch {
|
} catch {
|
||||||
Write-Log "Gruppe '$Group' konnte nicht abgerufen werden: $_" -Level WARN
|
Write-Log "Gruppe '$Group' konnte nicht abgerufen werden: $_" -Level WARN
|
||||||
return
|
return
|
||||||
@@ -126,17 +110,18 @@ function Start-Export {
|
|||||||
DisplayName, EmailAddress, Title, Department, Company,
|
DisplayName, EmailAddress, Title, Department, Company,
|
||||||
Enabled, LastLogonDate, PasswordLastSet, PasswordNeverExpires,
|
Enabled, LastLogonDate, PasswordLastSet, PasswordNeverExpires,
|
||||||
Manager, telephoneNumber, DistinguishedName `
|
Manager, telephoneNumber, DistinguishedName `
|
||||||
-ErrorAction Stop @adParams
|
-ErrorAction Stop @script:exportAdParams
|
||||||
|
|
||||||
$managerName = ''
|
$managerName = ''
|
||||||
if ($user.Manager) {
|
if ($user.Manager) {
|
||||||
try {
|
try {
|
||||||
$mgr = Get-ADUser -Identity $user.Manager -Properties DisplayName -ErrorAction Stop @adParams
|
$mgr = Get-ADUser -Identity $user.Manager -Properties DisplayName `
|
||||||
|
-ErrorAction Stop @script:exportAdParams
|
||||||
$managerName = $mgr.DisplayName
|
$managerName = $mgr.DisplayName
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
$results.Add([PSCustomObject]@{
|
$script:exportResults.Add([PSCustomObject]@{
|
||||||
SourceGroup = $SourceGroup
|
SourceGroup = $SourceGroup
|
||||||
MemberOfGroup = $Group
|
MemberOfGroup = $Group
|
||||||
SamAccountName = $user.SamAccountName
|
SamAccountName = $user.SamAccountName
|
||||||
@@ -161,30 +146,57 @@ function Start-Export {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'group' {
|
'group' {
|
||||||
Write-Log "Verschachtelte Gruppe: $($member.Name)" -Level INFO
|
Write-Log "Verschachtelte Gruppe: $($member.Name)"
|
||||||
Get-NestedMembers -Group $member.distinguishedName -SourceGroup $SourceGroup
|
Get-NestedMembers -Group $member.distinguishedName -SourceGroup $SourceGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Log "Starte Export für Gruppe '$GroupName' (inkl. verschachtelter Gruppen)..."
|
function Start-Export {
|
||||||
|
param([string]$GroupName, [string]$OutputPath, [string]$Server)
|
||||||
|
|
||||||
|
$script:btnExport.Enabled = $false
|
||||||
|
$script:rtbOutput.Clear()
|
||||||
|
|
||||||
|
if (-not (Initialize-ADModule)) {
|
||||||
|
$script:btnExport.Enabled = $true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:exportAdParams = @{}
|
||||||
|
if ($Server) { $script:exportAdParams['Server'] = $Server }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$null = Get-ADGroup -Identity $GroupName -ErrorAction Stop @script:exportAdParams
|
||||||
|
Write-Log "Gruppe gefunden: $GroupName"
|
||||||
|
} catch {
|
||||||
|
Write-Log "Gruppe '$GroupName' nicht gefunden: $_" -Level ERROR
|
||||||
|
$script:btnExport.Enabled = $true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:visitedGroups = [System.Collections.Generic.HashSet[string]]::new(
|
||||||
|
[System.StringComparer]::OrdinalIgnoreCase)
|
||||||
|
$script:exportResults = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||||
|
|
||||||
|
Write-Log "Starte Export für '$GroupName' (inkl. verschachtelter Gruppen)..."
|
||||||
Get-NestedMembers -Group $GroupName -SourceGroup $GroupName
|
Get-NestedMembers -Group $GroupName -SourceGroup $GroupName
|
||||||
|
|
||||||
if ($results.Count -eq 0) {
|
if ($script:exportResults.Count -eq 0) {
|
||||||
Write-Log "Keine Benutzer in Gruppe '$GroupName' gefunden." -Level WARN
|
Write-Log "Keine Benutzer in Gruppe '$GroupName' gefunden." -Level WARN
|
||||||
$script:btnExport.Enabled = $true
|
$script:btnExport.Enabled = $true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$unique = $results | Sort-Object SamAccountName -Unique
|
$unique = $script:exportResults | Sort-Object SamAccountName -Unique
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -Delimiter ';' -ErrorAction Stop
|
$unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -Delimiter ';' -ErrorAction Stop
|
||||||
$summary = "Export abgeschlossen. Benutzer: {0} | Gruppen durchsucht: {1} | Datei: {2}" -f `
|
$msg = "Export abgeschlossen. Benutzer: {0} | Gruppen: {1} | Datei: {2}" -f `
|
||||||
$unique.Count, $visitedGroups.Count, $OutputPath
|
$unique.Count, $script:visitedGroups.Count, $OutputPath
|
||||||
Write-Log $summary -Level INFO
|
Write-Log $msg
|
||||||
Write-OutputLine $summary ([System.Drawing.Color]::LightGreen)
|
Write-OutputLine $msg ([System.Drawing.Color]::LightGreen)
|
||||||
} catch {
|
} catch {
|
||||||
Write-Log "Fehler beim Schreiben der CSV: $_" -Level ERROR
|
Write-Log "Fehler beim Schreiben der CSV: $_" -Level ERROR
|
||||||
}
|
}
|
||||||
@@ -235,12 +247,13 @@ $btnLoadGroups.FlatAppearance.BorderSize = 0
|
|||||||
$form.Controls.Add($btnLoadGroups)
|
$form.Controls.Add($btnLoadGroups)
|
||||||
|
|
||||||
$btnLoadGroups.Add_Click({
|
$btnLoadGroups.Add_Click({
|
||||||
|
if (-not (Initialize-ADModule)) { return }
|
||||||
$btnLoadGroups.Text = "Lädt..."
|
$btnLoadGroups.Text = "Lädt..."
|
||||||
$btnLoadGroups.Enabled = $false
|
$btnLoadGroups.Enabled = $false
|
||||||
[System.Windows.Forms.Application]::DoEvents()
|
[System.Windows.Forms.Application]::DoEvents()
|
||||||
try {
|
try {
|
||||||
$adP = @{}
|
$adP = @{}
|
||||||
if ($txtServer.Text.Trim()) { $adP['Server'] = $txtServer.Text.Trim() }
|
if ($txtServer.Tag) { $adP['Server'] = $txtServer.Tag }
|
||||||
$groups = Get-ADGroup -Filter * @adP | Select-Object -ExpandProperty Name | Sort-Object
|
$groups = Get-ADGroup -Filter * @adP | Select-Object -ExpandProperty Name | Sort-Object
|
||||||
$cmbGroup.Items.Clear()
|
$cmbGroup.Items.Clear()
|
||||||
$cmbGroup.Items.AddRange($groups)
|
$cmbGroup.Items.AddRange($groups)
|
||||||
@@ -252,7 +265,7 @@ $btnLoadGroups.Add_Click({
|
|||||||
$btnLoadGroups.Enabled = $true
|
$btnLoadGroups.Enabled = $true
|
||||||
})
|
})
|
||||||
|
|
||||||
# --- Output file row ---
|
# --- CSV output row ---
|
||||||
$lblOut = New-Object System.Windows.Forms.Label
|
$lblOut = New-Object System.Windows.Forms.Label
|
||||||
$lblOut.Text = "CSV Datei:"
|
$lblOut.Text = "CSV Datei:"
|
||||||
$lblOut.Location = New-Object System.Drawing.Point($pad, 58)
|
$lblOut.Location = New-Object System.Drawing.Point($pad, 58)
|
||||||
@@ -265,7 +278,7 @@ $txtOut.Size = New-Object System.Drawing.Size(436, 22)
|
|||||||
$txtOut.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
$txtOut.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
||||||
$txtOut.ForeColor = [System.Drawing.Color]::White
|
$txtOut.ForeColor = [System.Drawing.Color]::White
|
||||||
$txtOut.BorderStyle = "FixedSingle"
|
$txtOut.BorderStyle = "FixedSingle"
|
||||||
$txtOut.Text = (Join-Path $PSScriptRoot "export.csv")
|
$txtOut.Text = if ($PSScriptRoot) { Join-Path $PSScriptRoot "export.csv" } else { "export.csv" }
|
||||||
$form.Controls.Add($txtOut)
|
$form.Controls.Add($txtOut)
|
||||||
|
|
||||||
$btnBrowse = New-Object System.Windows.Forms.Button
|
$btnBrowse = New-Object System.Windows.Forms.Button
|
||||||
@@ -283,9 +296,7 @@ $btnBrowse.Add_Click({
|
|||||||
$sfd.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
|
$sfd.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
|
||||||
$sfd.Title = "CSV-Datei speichern"
|
$sfd.Title = "CSV-Datei speichern"
|
||||||
$sfd.FileName = "export.csv"
|
$sfd.FileName = "export.csv"
|
||||||
if ($sfd.ShowDialog() -eq "OK") {
|
if ($sfd.ShowDialog() -eq "OK") { $txtOut.Text = $sfd.FileName }
|
||||||
$txtOut.Text = $sfd.FileName
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# --- Server row ---
|
# --- Server row ---
|
||||||
@@ -299,21 +310,22 @@ $txtServer = New-Object System.Windows.Forms.TextBox
|
|||||||
$txtServer.Location = New-Object System.Drawing.Point(104, 94)
|
$txtServer.Location = New-Object System.Drawing.Point(104, 94)
|
||||||
$txtServer.Size = New-Object System.Drawing.Size(436, 22)
|
$txtServer.Size = New-Object System.Drawing.Size(436, 22)
|
||||||
$txtServer.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
$txtServer.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
||||||
$txtServer.ForeColor = [System.Drawing.Color]::White
|
|
||||||
$txtServer.BorderStyle = "FixedSingle"
|
$txtServer.BorderStyle = "FixedSingle"
|
||||||
|
$txtServer.Tag = ''
|
||||||
|
$txtServer.Text = 'Domänencontroller (leer = Standard)'
|
||||||
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
||||||
$txtServer.Text = "Domänencontroller (leer = Standard)"
|
|
||||||
$form.Controls.Add($txtServer)
|
$form.Controls.Add($txtServer)
|
||||||
|
|
||||||
$txtServer.Add_GotFocus({
|
$txtServer.Add_GotFocus({
|
||||||
if ($txtServer.ForeColor -eq [System.Drawing.Color]::Gray) {
|
if ($txtServer.Tag -eq '') {
|
||||||
$txtServer.Text = ''
|
$txtServer.Text = ''
|
||||||
$txtServer.ForeColor = [System.Drawing.Color]::White
|
$txtServer.ForeColor = [System.Drawing.Color]::White
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$txtServer.Add_LostFocus({
|
$txtServer.Add_LostFocus({
|
||||||
if ([string]::IsNullOrWhiteSpace($txtServer.Text)) {
|
$txtServer.Tag = $txtServer.Text.Trim()
|
||||||
$txtServer.Text = "Domänencontroller (leer = Standard)"
|
if ($txtServer.Tag -eq '') {
|
||||||
|
$txtServer.Text = 'Domänencontroller (leer = Standard)'
|
||||||
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -340,14 +352,16 @@ $form.Controls.Add($script:btnExport)
|
|||||||
$script:btnExport.Add_Click({
|
$script:btnExport.Add_Click({
|
||||||
$groupName = $cmbGroup.Text.Trim()
|
$groupName = $cmbGroup.Text.Trim()
|
||||||
$outputPath = $txtOut.Text.Trim()
|
$outputPath = $txtOut.Text.Trim()
|
||||||
$server = if ($txtServer.ForeColor -ne [System.Drawing.Color]::Gray) { $txtServer.Text.Trim() } else { '' }
|
$server = $txtServer.Tag
|
||||||
|
|
||||||
if (-not $groupName) {
|
if (-not $groupName) {
|
||||||
[System.Windows.Forms.MessageBox]::Show("Bitte eine AD-Gruppe eingeben oder auswählen.", "Validierung", "OK", "Warning") | Out-Null
|
[System.Windows.Forms.MessageBox]::Show(
|
||||||
|
"Bitte eine AD-Gruppe eingeben oder auswählen.", "Validierung", "OK", "Warning") | Out-Null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (-not $outputPath) {
|
if (-not $outputPath) {
|
||||||
[System.Windows.Forms.MessageBox]::Show("Bitte einen Speicherpfad für die CSV angeben.", "Validierung", "OK", "Warning") | Out-Null
|
[System.Windows.Forms.MessageBox]::Show(
|
||||||
|
"Bitte einen Speicherpfad für die CSV angeben.", "Validierung", "OK", "Warning") | Out-Null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user