export script hinzugefügt
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
GUI tool to export all members of an AD group (including nested groups) to a CSV file.
|
||||
.PARAMETER LogPath
|
||||
Optional path for the log file. Defaults to script directory with a timestamp.
|
||||
#>
|
||||
|
||||
param (
|
||||
[string]$LogPath = (Join-Path $PSScriptRoot ("export_log_" + (Get-Date -Format 'yyyyMMdd_HHmmss') + ".txt"))
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ensure ActiveDirectory module is available
|
||||
# ---------------------------------------------------------------------------
|
||||
if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
if (-not $isAdmin) {
|
||||
Write-Error "ActiveDirectory module not found. Please run this script as Administrator to install it automatically, or install RSAT manually."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "ActiveDirectory module not found. Attempting to install..." -ForegroundColor Yellow
|
||||
$os = (Get-CimInstance Win32_OperatingSystem).Caption
|
||||
|
||||
if ($os -match "Server") {
|
||||
try {
|
||||
Import-Module ServerManager -ErrorAction Stop
|
||||
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 {
|
||||
try {
|
||||
$feature = Get-WindowsCapability -Name "Rsat.ActiveDirectory*" -Online -ErrorAction Stop |
|
||||
Where-Object State -ne Installed | Select-Object -First 1
|
||||
if ($feature) {
|
||||
Add-WindowsCapability -Name $feature.Name -Online -ErrorAction Stop | Out-Null
|
||||
Write-Host "ActiveDirectory module installed via Add-WindowsCapability." -ForegroundColor Green
|
||||
}
|
||||
} catch {
|
||||
Write-Error "Failed to install ActiveDirectory module on Windows client: $_"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error "ActiveDirectory module could not be loaded: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
function Write-Log {
|
||||
param([string]$Message, [string]$Level = 'INFO')
|
||||
$entry = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
|
||||
$entry | Out-File -FilePath $LogPath -Append -Encoding UTF8
|
||||
|
||||
$color = switch ($Level) {
|
||||
'ERROR' { [System.Drawing.Color]::Salmon }
|
||||
'WARN' { [System.Drawing.Color]::Goldenrod }
|
||||
default { [System.Drawing.Color]::White }
|
||||
}
|
||||
Write-OutputLine $entry $color
|
||||
}
|
||||
|
||||
function Write-OutputLine {
|
||||
param([string]$Text, [System.Drawing.Color]$Color = [System.Drawing.Color]::White)
|
||||
$script:rtbOutput.SelectionStart = $script:rtbOutput.TextLength
|
||||
$script:rtbOutput.SelectionLength = 0
|
||||
$script:rtbOutput.SelectionColor = $Color
|
||||
$script:rtbOutput.AppendText("$Text`n")
|
||||
$script:rtbOutput.ScrollToCaret()
|
||||
[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 {
|
||||
param([string]$Group, [string]$SourceGroup)
|
||||
|
||||
if (-not $visitedGroups.Add($Group)) { return }
|
||||
|
||||
try {
|
||||
$members = Get-ADGroupMember -Identity $Group -ErrorAction Stop @adParams
|
||||
} catch {
|
||||
Write-Log "Gruppe '$Group' konnte nicht abgerufen werden: $_" -Level WARN
|
||||
return
|
||||
}
|
||||
|
||||
foreach ($member in $members) {
|
||||
switch ($member.objectClass) {
|
||||
'user' {
|
||||
try {
|
||||
$user = Get-ADUser -Identity $member.distinguishedName -Properties `
|
||||
DisplayName, EmailAddress, Title, Department, Company,
|
||||
Enabled, LastLogonDate, PasswordLastSet, PasswordNeverExpires,
|
||||
Manager, telephoneNumber, DistinguishedName `
|
||||
-ErrorAction Stop @adParams
|
||||
|
||||
$managerName = ''
|
||||
if ($user.Manager) {
|
||||
try {
|
||||
$mgr = Get-ADUser -Identity $user.Manager -Properties DisplayName -ErrorAction Stop @adParams
|
||||
$managerName = $mgr.DisplayName
|
||||
} catch {}
|
||||
}
|
||||
|
||||
$results.Add([PSCustomObject]@{
|
||||
SourceGroup = $SourceGroup
|
||||
MemberOfGroup = $Group
|
||||
SamAccountName = $user.SamAccountName
|
||||
DisplayName = $user.DisplayName
|
||||
GivenName = $user.GivenName
|
||||
Surname = $user.Surname
|
||||
EmailAddress = $user.EmailAddress
|
||||
Title = $user.Title
|
||||
Department = $user.Department
|
||||
Company = $user.Company
|
||||
TelephoneNumber = $user.telephoneNumber
|
||||
Enabled = $user.Enabled
|
||||
LastLogonDate = $user.LastLogonDate
|
||||
PasswordLastSet = $user.PasswordLastSet
|
||||
PasswordNeverExpires = $user.PasswordNeverExpires
|
||||
Manager = $managerName
|
||||
DistinguishedName = $user.DistinguishedName
|
||||
})
|
||||
Write-Log " Benutzer: $($user.SamAccountName) ($($user.DisplayName))"
|
||||
} catch {
|
||||
Write-Log "Benutzer '$($member.SamAccountName)' konnte nicht abgerufen werden: $_" -Level WARN
|
||||
}
|
||||
}
|
||||
'group' {
|
||||
Write-Log "Verschachtelte Gruppe: $($member.Name)" -Level INFO
|
||||
Get-NestedMembers -Group $member.distinguishedName -SourceGroup $SourceGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Log "Starte Export für Gruppe '$GroupName' (inkl. verschachtelter Gruppen)..."
|
||||
Get-NestedMembers -Group $GroupName -SourceGroup $GroupName
|
||||
|
||||
if ($results.Count -eq 0) {
|
||||
Write-Log "Keine Benutzer in Gruppe '$GroupName' gefunden." -Level WARN
|
||||
$script:btnExport.Enabled = $true
|
||||
return
|
||||
}
|
||||
|
||||
$unique = $results | Sort-Object SamAccountName -Unique
|
||||
|
||||
try {
|
||||
$unique | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -Delimiter ';' -ErrorAction Stop
|
||||
$summary = "Export abgeschlossen. Benutzer: {0} | Gruppen durchsucht: {1} | Datei: {2}" -f `
|
||||
$unique.Count, $visitedGroups.Count, $OutputPath
|
||||
Write-Log $summary -Level INFO
|
||||
Write-OutputLine $summary ([System.Drawing.Color]::LightGreen)
|
||||
} catch {
|
||||
Write-Log "Fehler beim Schreiben der CSV: $_" -Level ERROR
|
||||
}
|
||||
|
||||
$script:btnExport.Enabled = $true
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Form
|
||||
# ---------------------------------------------------------------------------
|
||||
$form = New-Object System.Windows.Forms.Form
|
||||
$form.Text = "AD Group Export"
|
||||
$form.Size = New-Object System.Drawing.Size(680, 600)
|
||||
$form.StartPosition = "CenterScreen"
|
||||
$form.FormBorderStyle = "FixedDialog"
|
||||
$form.MaximizeBox = $false
|
||||
$form.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30)
|
||||
$form.ForeColor = [System.Drawing.Color]::White
|
||||
$form.Font = New-Object System.Drawing.Font('Segoe UI', 9)
|
||||
|
||||
$pad = 16
|
||||
|
||||
# --- Group row ---
|
||||
$lblGroup = New-Object System.Windows.Forms.Label
|
||||
$lblGroup.Text = "AD Gruppe:"
|
||||
$lblGroup.Location = New-Object System.Drawing.Point($pad, 20)
|
||||
$lblGroup.Size = New-Object System.Drawing.Size(84, 22)
|
||||
$form.Controls.Add($lblGroup)
|
||||
|
||||
$cmbGroup = New-Object System.Windows.Forms.ComboBox
|
||||
$cmbGroup.Location = New-Object System.Drawing.Point(104, 18)
|
||||
$cmbGroup.Size = New-Object System.Drawing.Size(436, 22)
|
||||
$cmbGroup.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
||||
$cmbGroup.ForeColor = [System.Drawing.Color]::White
|
||||
$cmbGroup.FlatStyle = "Flat"
|
||||
$cmbGroup.AutoCompleteMode = "SuggestAppend"
|
||||
$cmbGroup.AutoCompleteSource = "ListItems"
|
||||
$form.Controls.Add($cmbGroup)
|
||||
|
||||
$btnLoadGroups = New-Object System.Windows.Forms.Button
|
||||
$btnLoadGroups.Text = "Gruppen laden"
|
||||
$btnLoadGroups.Location = New-Object System.Drawing.Point(550, 16)
|
||||
$btnLoadGroups.Size = New-Object System.Drawing.Size(96, 26)
|
||||
$btnLoadGroups.BackColor = [System.Drawing.Color]::FromArgb(0, 122, 204)
|
||||
$btnLoadGroups.ForeColor = [System.Drawing.Color]::White
|
||||
$btnLoadGroups.FlatStyle = "Flat"
|
||||
$btnLoadGroups.FlatAppearance.BorderSize = 0
|
||||
$form.Controls.Add($btnLoadGroups)
|
||||
|
||||
$btnLoadGroups.Add_Click({
|
||||
$btnLoadGroups.Text = "Lädt..."
|
||||
$btnLoadGroups.Enabled = $false
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
try {
|
||||
$adP = @{}
|
||||
if ($txtServer.Text.Trim()) { $adP['Server'] = $txtServer.Text.Trim() }
|
||||
$groups = Get-ADGroup -Filter * @adP | Select-Object -ExpandProperty Name | Sort-Object
|
||||
$cmbGroup.Items.Clear()
|
||||
$cmbGroup.Items.AddRange($groups)
|
||||
Write-OutputLine "$($groups.Count) Gruppen geladen." ([System.Drawing.Color]::LightGreen)
|
||||
} catch {
|
||||
Write-OutputLine "Fehler beim Laden der Gruppen: $_" ([System.Drawing.Color]::Salmon)
|
||||
}
|
||||
$btnLoadGroups.Text = "Gruppen laden"
|
||||
$btnLoadGroups.Enabled = $true
|
||||
})
|
||||
|
||||
# --- Output file row ---
|
||||
$lblOut = New-Object System.Windows.Forms.Label
|
||||
$lblOut.Text = "CSV Datei:"
|
||||
$lblOut.Location = New-Object System.Drawing.Point($pad, 58)
|
||||
$lblOut.Size = New-Object System.Drawing.Size(84, 22)
|
||||
$form.Controls.Add($lblOut)
|
||||
|
||||
$txtOut = New-Object System.Windows.Forms.TextBox
|
||||
$txtOut.Location = New-Object System.Drawing.Point(104, 56)
|
||||
$txtOut.Size = New-Object System.Drawing.Size(436, 22)
|
||||
$txtOut.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
||||
$txtOut.ForeColor = [System.Drawing.Color]::White
|
||||
$txtOut.BorderStyle = "FixedSingle"
|
||||
$txtOut.Text = (Join-Path $PSScriptRoot "export.csv")
|
||||
$form.Controls.Add($txtOut)
|
||||
|
||||
$btnBrowse = New-Object System.Windows.Forms.Button
|
||||
$btnBrowse.Text = "Speichern..."
|
||||
$btnBrowse.Location = New-Object System.Drawing.Point(550, 54)
|
||||
$btnBrowse.Size = New-Object System.Drawing.Size(96, 26)
|
||||
$btnBrowse.BackColor = [System.Drawing.Color]::FromArgb(0, 122, 204)
|
||||
$btnBrowse.ForeColor = [System.Drawing.Color]::White
|
||||
$btnBrowse.FlatStyle = "Flat"
|
||||
$btnBrowse.FlatAppearance.BorderSize = 0
|
||||
$form.Controls.Add($btnBrowse)
|
||||
|
||||
$btnBrowse.Add_Click({
|
||||
$sfd = New-Object System.Windows.Forms.SaveFileDialog
|
||||
$sfd.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
|
||||
$sfd.Title = "CSV-Datei speichern"
|
||||
$sfd.FileName = "export.csv"
|
||||
if ($sfd.ShowDialog() -eq "OK") {
|
||||
$txtOut.Text = $sfd.FileName
|
||||
}
|
||||
})
|
||||
|
||||
# --- Server row ---
|
||||
$lblServer = New-Object System.Windows.Forms.Label
|
||||
$lblServer.Text = "DC (optional):"
|
||||
$lblServer.Location = New-Object System.Drawing.Point($pad, 96)
|
||||
$lblServer.Size = New-Object System.Drawing.Size(84, 22)
|
||||
$form.Controls.Add($lblServer)
|
||||
|
||||
$txtServer = New-Object System.Windows.Forms.TextBox
|
||||
$txtServer.Location = New-Object System.Drawing.Point(104, 94)
|
||||
$txtServer.Size = New-Object System.Drawing.Size(436, 22)
|
||||
$txtServer.BackColor = [System.Drawing.Color]::FromArgb(45, 45, 45)
|
||||
$txtServer.ForeColor = [System.Drawing.Color]::White
|
||||
$txtServer.BorderStyle = "FixedSingle"
|
||||
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
||||
$txtServer.Text = "Domänencontroller (leer = Standard)"
|
||||
$form.Controls.Add($txtServer)
|
||||
|
||||
$txtServer.Add_GotFocus({
|
||||
if ($txtServer.ForeColor -eq [System.Drawing.Color]::Gray) {
|
||||
$txtServer.Text = ''
|
||||
$txtServer.ForeColor = [System.Drawing.Color]::White
|
||||
}
|
||||
})
|
||||
$txtServer.Add_LostFocus({
|
||||
if ([string]::IsNullOrWhiteSpace($txtServer.Text)) {
|
||||
$txtServer.Text = "Domänencontroller (leer = Standard)"
|
||||
$txtServer.ForeColor = [System.Drawing.Color]::Gray
|
||||
}
|
||||
})
|
||||
|
||||
# --- Separator ---
|
||||
$sep = New-Object System.Windows.Forms.Panel
|
||||
$sep.Location = New-Object System.Drawing.Point($pad, 130)
|
||||
$sep.Size = New-Object System.Drawing.Size(632, 1)
|
||||
$sep.BackColor = [System.Drawing.Color]::FromArgb(70, 70, 70)
|
||||
$form.Controls.Add($sep)
|
||||
|
||||
# --- Export button ---
|
||||
$script:btnExport = New-Object System.Windows.Forms.Button
|
||||
$script:btnExport.Text = "Export starten"
|
||||
$script:btnExport.Location = New-Object System.Drawing.Point($pad, 140)
|
||||
$script:btnExport.Size = New-Object System.Drawing.Size(632, 34)
|
||||
$script:btnExport.BackColor = [System.Drawing.Color]::FromArgb(16, 124, 16)
|
||||
$script:btnExport.ForeColor = [System.Drawing.Color]::White
|
||||
$script:btnExport.FlatStyle = "Flat"
|
||||
$script:btnExport.FlatAppearance.BorderSize = 0
|
||||
$script:btnExport.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold)
|
||||
$form.Controls.Add($script:btnExport)
|
||||
|
||||
$script:btnExport.Add_Click({
|
||||
$groupName = $cmbGroup.Text.Trim()
|
||||
$outputPath = $txtOut.Text.Trim()
|
||||
$server = if ($txtServer.ForeColor -ne [System.Drawing.Color]::Gray) { $txtServer.Text.Trim() } else { '' }
|
||||
|
||||
if (-not $groupName) {
|
||||
[System.Windows.Forms.MessageBox]::Show("Bitte eine AD-Gruppe eingeben oder auswählen.", "Validierung", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
if (-not $outputPath) {
|
||||
[System.Windows.Forms.MessageBox]::Show("Bitte einen Speicherpfad für die CSV angeben.", "Validierung", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
Start-Export -GroupName $groupName -OutputPath $outputPath -Server $server
|
||||
})
|
||||
|
||||
# --- Output panel ---
|
||||
$lblOutput = New-Object System.Windows.Forms.Label
|
||||
$lblOutput.Text = "Output:"
|
||||
$lblOutput.Location = New-Object System.Drawing.Point($pad, 186)
|
||||
$lblOutput.Size = New-Object System.Drawing.Size(80, 18)
|
||||
$form.Controls.Add($lblOutput)
|
||||
|
||||
$script:rtbOutput = New-Object System.Windows.Forms.RichTextBox
|
||||
$script:rtbOutput.Location = New-Object System.Drawing.Point($pad, 206)
|
||||
$script:rtbOutput.Size = New-Object System.Drawing.Size(632, 320)
|
||||
$script:rtbOutput.BackColor = [System.Drawing.Color]::FromArgb(12, 12, 12)
|
||||
$script:rtbOutput.ForeColor = [System.Drawing.Color]::White
|
||||
$script:rtbOutput.Font = New-Object System.Drawing.Font('Consolas', 9)
|
||||
$script:rtbOutput.ReadOnly = $true
|
||||
$script:rtbOutput.BorderStyle = "None"
|
||||
$script:rtbOutput.ScrollBars = "Vertical"
|
||||
$form.Controls.Add($script:rtbOutput)
|
||||
|
||||
# --- Status bar ---
|
||||
$lblStatus = New-Object System.Windows.Forms.Label
|
||||
$lblStatus.Text = "Log: $LogPath"
|
||||
$lblStatus.Location = New-Object System.Drawing.Point($pad, 536)
|
||||
$lblStatus.Size = New-Object System.Drawing.Size(632, 18)
|
||||
$lblStatus.ForeColor = [System.Drawing.Color]::Gray
|
||||
$lblStatus.Font = New-Object System.Drawing.Font('Segoe UI', 8)
|
||||
$form.Controls.Add($lblStatus)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
$form.ShowDialog() | Out-Null
|
||||
$form.Dispose()
|
||||
Reference in New Issue
Block a user