Files
wendeIT-scripting/Intune/Intune-App-Manager-Web/Start.ps1
T
2026-06-17 09:41:43 +01:00

222 lines
9.2 KiB
PowerShell

# Intune Manager - Web Edition
# Startet einen lokalen HTTP-Server und oeffnet das moderne Web-Frontend.
#
# Aufruf:
# .\Start.ps1
# .\Start.ps1 -Port 8088 -NoBrowser
# .\Start.ps1 -TenantId "..." -ClientId "..."
[CmdletBinding()]
param(
[int]$Port = 8077,
# Optional: ueberschreibt die persistierten Settings nur fuer diesen Lauf.
# Wenn nicht gesetzt -> Werte aus %APPDATA%\IntuneAppManager-Web\settings.json
# (bzw. den Defaults beim Erststart) werden verwendet.
[string]$TenantId = "",
[string]$ClientId = "",
[switch]$NoBrowser,
[switch]$AutoInstall, # ohne Rueckfrage installieren
[switch]$SkipModuleCheck # Pre-Flight ueberspringen (z.B. CI)
)
$ErrorActionPreference = "Stop"
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
# ============================================================
# Pre-Flight: PowerShell-Module pruefen / installieren
# (nur User-Scope, nichts systemweites)
# ============================================================
function Test-RequiredModules {
[CmdletBinding()]
param(
[hashtable[]]$Required,
[switch]$AutoInstall
)
Write-Host "Pruefe PowerShell-Module..." -ForegroundColor Cyan
$missing = @()
$outdated = @()
foreach ($req in $Required) {
$name = $req.Name
$minV = [version]$req.MinVersion
$available = @(Get-Module -Name $name -ListAvailable -ErrorAction SilentlyContinue)
if ($available.Count -eq 0) {
Write-Host " [FEHLT] $name (>= $minV)" -ForegroundColor Yellow
$missing += $req
} else {
$maxV = ($available | Sort-Object Version -Descending | Select-Object -First 1).Version
if ($maxV -lt $minV) {
Write-Host " [ALT] $name v$maxV (<$minV)" -ForegroundColor Yellow
$outdated += @{ Name = $name; Have = $maxV; Need = $minV }
} else {
Write-Host " [OK] $name v$maxV" -ForegroundColor Green
}
}
}
if ($missing.Count -eq 0 -and $outdated.Count -eq 0) {
return $true
}
# NuGet-Provider sicherstellen, sonst schlaegt Install-Module schweigend fehl
$nuget = Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue
if (-not $nuget -or $nuget.Version -lt [version]"2.8.5.201") {
Write-Host ""
Write-Host "NuGet-PackageProvider fehlt — wird im User-Scope nachgezogen..." -ForegroundColor Yellow
try {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Scope CurrentUser -Force -ErrorAction Stop | Out-Null
} catch {
throw "NuGet-Provider konnte nicht installiert werden: $($_.Exception.Message)"
}
}
# PSGallery als trusted markieren, sonst kommt fuer jede Install-Aktion ein Prompt
$gallery = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue
if ($gallery -and $gallery.InstallationPolicy -ne 'Trusted') {
try { Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction Stop } catch {}
}
if (-not $AutoInstall) {
Write-Host ""
Write-Host "Folgendes wird im User-Scope (CurrentUser) installiert/aktualisiert:" -ForegroundColor Cyan
foreach ($m in $missing) { Write-Host " + $($m.Name) min. $($m.MinVersion)" -ForegroundColor White }
foreach ($o in $outdated) { Write-Host " ~ $($o.Name) v$($o.Have) -> min. $($o.Need)" -ForegroundColor White }
Write-Host ""
$answer = Read-Host "Jetzt automatisch installieren? [J]a / [N]ein"
if ($answer -notmatch '^(j|J|y|Y)') {
Write-Host ""
Write-Host "Abbruch. Du kannst die Module manuell installieren:" -ForegroundColor Yellow
foreach ($m in $missing) { Write-Host " Install-Module $($m.Name) -Scope CurrentUser -Force" -ForegroundColor Gray }
foreach ($o in $outdated) { Write-Host " Update-Module $($o.Name) -Scope CurrentUser -Force" -ForegroundColor Gray }
return $false
}
}
foreach ($m in $missing) {
Write-Host ""
Write-Host "Installiere $($m.Name) (CurrentUser)..." -ForegroundColor Cyan
try {
Install-Module -Name $m.Name -MinimumVersion $m.MinVersion -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
Write-Host " -> OK" -ForegroundColor Green
} catch {
Write-Host " -> Fehler: $($_.Exception.Message)" -ForegroundColor Red
return $false
}
}
foreach ($o in $outdated) {
Write-Host ""
Write-Host "Aktualisiere $($o.Name) auf min. $($o.Need)..." -ForegroundColor Cyan
try {
Install-Module -Name $o.Name -MinimumVersion $o.Need -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
Write-Host " -> OK" -ForegroundColor Green
} catch {
Write-Host " -> Fehler: $($_.Exception.Message)" -ForegroundColor Red
return $false
}
}
return $true
}
if (-not $SkipModuleCheck) {
$required = @(
@{ Name = "Microsoft.Graph.Authentication"; MinVersion = "2.0.0" }
)
if (-not (Test-RequiredModules -Required $required -AutoInstall:$AutoInstall)) {
Write-Host ""
Write-Host "Server-Start abgebrochen — benoetigte Module fehlen." -ForegroundColor Red
exit 1
}
Write-Host ""
}
# Quellmodule laden (Reihenfolge wichtig)
. (Join-Path $root "src/Models.ps1")
. (Join-Path $root "src/Graph.ps1")
. (Join-Path $root "src/Api.ps1")
. (Join-Path $root "src/Router.ps1")
. (Join-Path $root "src/Server.ps1")
# Settings aus Persistenz (Datei) laden, optional per Parameter ueberschreiben
$script:Settings = Read-Settings
if ($TenantId) { $script:Settings.connection.tenantId = $TenantId }
if ($ClientId) { $script:Settings.connection.clientId = $ClientId }
# Globale Konfiguration. TenantId/ClientId/Scopes spiegeln die Settings —
# Connect-Endpoint und Co. lesen weiterhin $script:Config, das nach jedem
# Settings-Save aktualisiert wird.
$script:Config = @{
TenantId = $script:Settings.connection.tenantId
ClientId = $script:Settings.connection.clientId
Scopes = @($script:Settings.connection.scopes)
WebRoot = (Join-Path $root "www")
ReportDir = (Join-Path $env:TEMP "IntuneAppManager-Reports")
}
if (-not (Test-Path $script:Config.ReportDir)) {
New-Item -ItemType Directory -Path $script:Config.ReportDir -Force | Out-Null
}
# Session-State (im PowerShell-Prozess gehalten)
$script:State = [pscustomobject]@{
Connected = $false
Account = $null
TenantId = $null
Groups = @() # abt-hm Gruppen
RpaGroups = @()
Apps = @()
Users = @{} # cache by id
GroupMembers = @{} # cache groupId -> member ids
Session = @() # geplante Zuweisungen
}
$script:ToolVersion = "0.1.23"
$script:BuildStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host ""
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host " Intune Manager - Web Edition" -ForegroundColor Cyan
Write-Host " Version: $script:ToolVersion" -ForegroundColor Green
Write-Host " BUILD: $script:BuildStamp" -ForegroundColor DarkGray
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""
# ============================================================
# Alte Instanzen beenden: laeuft das Tool bereits, haelt der alte Prozess den
# Port (HttpListener via http.sys) — ein zweiter Start scheitert dann still und
# das alte Fenster bedient weiter mit altem Code. Darum alle anderen Start.ps1-
# Prozesse DIESES Pfads vorher beenden, damit ein Neustart wirklich neu laedt.
# ============================================================
try {
$thisScript = $PSCommandPath
if ([string]::IsNullOrWhiteSpace($thisScript)) { $thisScript = Join-Path $PSScriptRoot 'Start.ps1' }
# Nur echte Server-Instanzen treffen: per -File gestartet UND auf diesen
# Start.ps1-Pfad zeigend. So werden -Command-Prozesse (Diagnose etc.), die
# den Pfad nur als String enthalten, NICHT versehentlich beendet.
$stale = @(Get-CimInstance Win32_Process -Filter "Name='powershell.exe' OR Name='pwsh.exe'" -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessId -ne $PID -and $_.CommandLine -and ($_.CommandLine -like "*$thisScript*") -and ($_.CommandLine -like "*-File*") })
foreach ($p in $stale) {
Write-Host " Beende alte Instanz (PID $($p.ProcessId))..." -ForegroundColor Yellow
try { Stop-Process -Id $p.ProcessId -Force -ErrorAction Stop }
catch { Write-Host " konnte PID $($p.ProcessId) nicht beenden: $($_.Exception.Message)" -ForegroundColor DarkYellow }
}
if ($stale.Count -gt 0) { Start-Sleep -Milliseconds 800 } # kurz warten bis der Port frei ist
} catch {
Write-Host " (Konnte alte Instanzen nicht pruefen: $($_.Exception.Message))" -ForegroundColor DarkGray
}
$url = "http://localhost:$Port/"
Write-Host " URL: $url" -ForegroundColor Green
Write-Host " WebRoot: $($script:Config.WebRoot)" -ForegroundColor DarkGray
Write-Host " Reports: $($script:Config.ReportDir)" -ForegroundColor DarkGray
Write-Host " Beenden: Ctrl+C" -ForegroundColor DarkGray
Write-Host ""
if (-not $NoBrowser) {
Start-Process $url
}
Start-WebServer -Port $Port