commit fedd8898d7ea171874029c415e834548d527d5e6 Author: Marco Wende Date: Wed Jun 17 07:17:35 2026 +0200 new diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..dd36122 Binary files /dev/null and b/.DS_Store differ diff --git a/Intune/Intune-App-Assignment-Application-v5_2_0_MW.ps1 b/Intune/Intune-App-Assignment-Application-v5_2_0_MW.ps1 new file mode 100644 index 0000000..ea85930 --- /dev/null +++ b/Intune/Intune-App-Assignment-Application-v5_2_0_MW.ps1 @@ -0,0 +1,4019 @@ +# Intune App-Zuweisungs-Manager - Erweiterte Version (v5.2.0 - Gruppenabfrage hinzugefügt) +# Mit App-Suche, Filter, Required-Gruppen erstellen, Session-Übersicht, RPA-Tab, Gruppenabfrage und target-spezifischen Zuweisungen + +Add-Type -AssemblyName PresentationFramework +Add-Type -AssemblyName System.Windows.Forms + +# Funktion zum Laden des Microsoft.Graph.Authentication Moduls (wird beim Connect aufgerufen) +# NUR dieses Modul wird benötigt - alle anderen Aufrufe gehen über Invoke-MgGraphRequest +function Initialize-GraphModules { + if (Get-Module Microsoft.Graph.Authentication) { + return $true + } + + try { + Import-Module Microsoft.Graph.Authentication -ErrorAction Stop + return $true + } catch { + [System.Windows.MessageBox]::Show( + "Fehler beim Laden von Microsoft.Graph.Authentication:`n$($_.Exception.Message)`n`nBitte führen Sie aus:`nInstall-Module Microsoft.Graph.Authentication -Force", + "Modul-Fehler", "OK", "Error") + return $false + } +} + +# ===== GRAPH API HILFSFUNKTIONEN ===== +# Diese ersetzen die Microsoft.Graph.* Cmdlets um Versionskonflikte zu vermeiden + +function Get-GraphGroup { + param( + [string]$GroupId, + [string]$Filter, + [string[]]$Property, + [switch]$All + ) + + try { + $select = if ($Property) { "&`$select=" + ($Property -join ",").ToLower() } else { "" } + + if ($GroupId) { + $uri = "https://graph.microsoft.com/v1.0/groups/$GroupId`?`$select=" + ($Property -join ",").ToLower() + return Invoke-MgGraphRequest -Uri $uri -Method GET + } + else { + $uri = "https://graph.microsoft.com/v1.0/groups" + if ($Filter) { $uri += "?`$filter=$Filter$select" } + elseif ($select) { $uri += "?$($select.TrimStart('&'))" } + + $results = @() + do { + $response = Invoke-MgGraphRequest -Uri $uri -Method GET + if ($response.value) { $results += $response.value } + $uri = $response.'@odata.nextLink' + } while ($All -and $uri) + + return $results + } + } catch { + Write-Warning "Get-GraphGroup Fehler: $_" + return $null + } +} + +function Get-GraphGroupMember { + param([string]$GroupId, [switch]$All) + + try { + $uri = "https://graph.microsoft.com/v1.0/groups/$GroupId/members" + $results = @() + do { + $response = Invoke-MgGraphRequest -Uri $uri -Method GET + if ($response.value) { $results += $response.value } + $uri = $response.'@odata.nextLink' + } while ($All -and $uri) + + return $results + } catch { + Write-Warning "Get-GraphGroupMember Fehler: $_" + return @() + } +} + +function Find-GraphGroupsByName { + # Sucht beliebige Gruppen im Tenant per Teilstring (nicht nur Präfix wie Get-GraphGroup -Filter startswith) + param([string]$SearchTerm) + + try { + $encoded = $SearchTerm.Replace("'", "''") + $uri = "https://graph.microsoft.com/v1.0/groups?`$filter=contains(displayName,'$encoded')&`$count=true&`$top=999&`$select=id,displayName" + $results = @() + do { + $response = Invoke-MgGraphRequest -Uri $uri -Method GET -Headers @{ ConsistencyLevel = "eventual" } + if ($response.value) { $results += $response.value } + $uri = $response.'@odata.nextLink' + } while ($uri) + + return $results + } catch { + Write-Warning "Find-GraphGroupsByName Fehler: $_" + return @() + } +} + +function Get-ResolvedGroupMembers { + # Liest die Mitglieder einer Gruppe aus (User, Geräte, ggf. verschachtelte Gruppen) + param( + [string]$GroupId, + [string]$SourceGroupName, + [switch]$ResolveNested, + [System.Collections.Generic.HashSet[string]]$Visited + ) + + if (-not $Visited) { $Visited = [System.Collections.Generic.HashSet[string]]::new() } + if ($Visited.Contains($GroupId)) { return @() } + [void]$Visited.Add($GroupId) + + $members = Get-GraphGroupMember -GroupId $GroupId -All + $results = [System.Collections.Generic.List[object]]::new() + + foreach ($m in $members) { + $odataType = $m.'@odata.type' + + switch ($odataType) { + '#microsoft.graph.user' { + $results.Add([PSCustomObject]@{ + DisplayName = $m.displayName + UserPrincipalName = $m.userPrincipalName + Id = $m.id + ObjectType = "Benutzer" + SourceGroup = $SourceGroupName + }) + } + '#microsoft.graph.device' { + $results.Add([PSCustomObject]@{ + DisplayName = $m.displayName + UserPrincipalName = "" + Id = $m.id + ObjectType = "Gerät" + SourceGroup = $SourceGroupName + }) + } + '#microsoft.graph.group' { + if ($ResolveNested) { + $nested = Get-ResolvedGroupMembers -GroupId $m.id -SourceGroupName $m.displayName -ResolveNested -Visited $Visited + foreach ($n in $nested) { $results.Add($n) } + } + else { + $results.Add([PSCustomObject]@{ + DisplayName = $m.displayName + UserPrincipalName = "" + Id = $m.id + ObjectType = "Gruppe (nicht aufgelöst)" + SourceGroup = $SourceGroupName + }) + } + } + default { + $results.Add([PSCustomObject]@{ + DisplayName = $m.displayName + UserPrincipalName = "" + Id = $m.id + ObjectType = "Andere ($odataType)" + SourceGroup = $SourceGroupName + }) + } + } + } + return $results +} + +function Get-GraphUser { + param( + [string]$UserId, + [string]$Filter, + [string[]]$Property, + [switch]$All + ) + + try { + $select = if ($Property) { "`$select=" + ($Property -join ",").ToLower() } else { "" } + + if ($UserId) { + $uri = "https://graph.microsoft.com/v1.0/users/$UserId" + if ($select) { $uri += "?$select" } + return Invoke-MgGraphRequest -Uri $uri -Method GET + } + else { + $uri = "https://graph.microsoft.com/v1.0/users" + $params = @() + if ($Filter) { $params += "`$filter=$Filter" } + if ($select) { $params += $select } + if ($params) { $uri += "?" + ($params -join "&") } + + $results = @() + do { + $response = Invoke-MgGraphRequest -Uri $uri -Method GET + if ($response.value) { $results += $response.value } + $uri = $response.'@odata.nextLink' + } while ($All -and $uri) + + return $results + } + } catch { + Write-Warning "Get-GraphUser Fehler: $_" + return $null + } +} + +function New-GraphGroup { + param( + [string]$DisplayName, + [bool]$MailEnabled = $false, + [bool]$SecurityEnabled = $true, + [string]$MailNickname + ) + + $body = @{ + displayName = $DisplayName + mailEnabled = $MailEnabled + securityEnabled = $SecurityEnabled + mailNickname = $MailNickname + } + + return Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/groups" -Method POST -Body $body +} + +function Add-GraphGroupMember { + param([string]$GroupId, [string]$DirectoryObjectId) + + $body = @{ + "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$DirectoryObjectId" + } + + Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/groups/$GroupId/members/`$ref" -Method POST -Body $body +} + +function New-AssignmentReport { + param( + $Targets, + $Assignments, + $IsUserMode, + $SuccessCount, + $ErrorCount, + $SkippedCount, + $DetailedResults + ) + + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $dateForFile = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" + $targetType = if ($IsUserMode) { "Benutzer" } else { "Gruppen/Abteilungen" } + $executedBy = $env:USERNAME + $totalOps = $SuccessCount + $ErrorCount + $SkippedCount + + # Gruppiere Assignments nach Typ + $availableApps = @($Assignments | Where-Object { $_.Type -eq "Available" }) + $requiredApps = @($Assignments | Where-Object { $_.Type -eq "Required" }) + + $html = @" + + + + + + Intune Zuweisungs-Report - $dateForFile + + + +
+
+

Intune App-Zuweisungs-Report

+
HanseMerkur | Automatisierte App-Zuweisung
+
+ +
+
+ Datum & Uhrzeit + $timestamp +
+
+ Ausgefuhrt von + $executedBy +
+
+ Ziel-Typ + $targetType +
+
+ Anzahl Ziele + $($Targets.Count) +
+
+ +
+
+
$totalOps
+
Gesamt
+
+
+
$SuccessCount
+
Erfolgreich
+
+
+
$SkippedCount
+
Ubersprungen
+
+
+
$ErrorCount
+
Fehler
+
+
+ +
+
Zugewiesene $targetType
+
+"@ + + foreach ($target in $Targets) { + $html += " $($target.DisplayName)`n" + } + + $html += @" +
+
+ +
+
+ Available-Zuweisungen + $($availableApps.Count) Apps +
+"@ + + if ($availableApps.Count -gt 0) { + $html += @" + + + + + + + + +"@ + foreach ($app in $availableApps) { + $html += " `n" + } + $html += "
App-NameZiel-Gruppe
$($app.AppName)$($app.GroupName)
`n" + } else { + $html += "
Keine Available-Zuweisungen
`n" + } + + $html += @" +
+ +
+
+ Required-Zuweisungen + $($requiredApps.Count) Apps +
+"@ + + if ($requiredApps.Count -gt 0) { + $html += @" + + + + + + + + +"@ + foreach ($app in $requiredApps) { + $html += " `n" + } + $html += "
App-NameZiel-Gruppe
$($app.AppName)$($app.GroupName)
`n" + } else { + $html += "
Keine Required-Zuweisungen
`n" + } + + $html += @" +
+ + +
+ + +"@ + + # Report auf Desktop speichern + $desktopPath = [Environment]::GetFolderPath("Desktop") + $reportPath = Join-Path $desktopPath "Intune-Zuweisungs-Report_$dateForFile.html" + + $html | Out-File -FilePath $reportPath -Encoding UTF8 + + return $reportPath +} + +$xaml = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +