#Requires -Version 5.1
<#
IT Admin Tool - GUI nastroj pro spravu Windows stanic
- Hromadna instalace aplikaci (winget)
- Odinstalace bloatware (Appx)
- Uzitecna systemova nastaveni
- Diagnosticke prikazy (chkdsk, sfc, dism...)
Firemni barvy: #0062ae (modra), #ffc627 (zluta)
#>
# ---------------------------------------------------------------------------
# 0) Elevace - skript musi bezet jako Administrator
# ---------------------------------------------------------------------------
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process powershell.exe -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
exit
}
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing
# Vlastni AppUserModelID - bez tohoto Windows casto na panelu uloh vezme ikonu
# primo z powershell.exe (identitu okna odvozuje od spustitelneho souboru procesu),
# bez ohledu na to, co nastavime pres Window.Icon / WM_SETICON.
Add-Type -Name Win32Aumid -Namespace ITAdminTool -MemberDefinition @'
[DllImport("shell32.dll", SetLastError = true)]
public static extern int SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID);
'@
[ITAdminTool.Win32Aumid]::SetCurrentProcessExplicitAppUserModelID("KETNET.ITAdminTool") | Out-Null
# Skryti konzoloveho okna powershell.exe (to je puvod PS ikony na panelu ulom) -
# WPF okno si pak na panelu jede jako samostatna, vlastni polozka.
Add-Type -Name Win32Console -Namespace ITAdminTool -MemberDefinition @'
[DllImport("kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
'@
$consoleHwnd = [ITAdminTool.Win32Console]::GetConsoleWindow()
if ($consoleHwnd -ne [IntPtr]::Zero) {
[ITAdminTool.Win32Console]::ShowWindow($consoleHwnd, 0) | Out-Null # SW_HIDE
}
# ---------------------------------------------------------------------------
# 1) Logovani
# ---------------------------------------------------------------------------
$Global:LogPath = Join-Path $env:TEMP "ITAdminTool_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
$Global:OdtDir = "C:\ITAdminTool\ODT"
$Global:IconPath = Join-Path $env:LOCALAPPDATA "ITAdminTool\icon.ico"
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$line = "[{0}] [{1}] {2}" -f (Get-Date -Format 'HH:mm:ss'), $Level, $Message
Add-Content -Path $Global:LogPath -Value $line -Encoding UTF8
if ($Global:LogBox) {
$Global:LogBox.Dispatcher.Invoke([action]{
$Global:LogBox.AppendText("$line`r`n")
$Global:LogBox.ScrollToEnd()
})
}
}
# ---------------------------------------------------------------------------
# 2) XAML - definice okna (barvy firmy: modra #0062ae, zluta #ffc627)
# ---------------------------------------------------------------------------
[xml]$xaml = @'
'@
$reader = New-Object System.Xml.XmlNodeReader $xaml
$window = [Windows.Markup.XamlReader]::Load($reader)
# ---------------------------------------------------------------------------
# 2b) Vlastni ikona (vygenerovana ze znacky loga, misto vychozi PowerShell ikony)
# ---------------------------------------------------------------------------
function New-LogoIcon {
param([string]$OutIcoPath, [int]$Size = 256)
$canvas = New-Object System.Windows.Controls.Canvas
$canvas.Width = 74.34
$canvas.Height = 74.34
$canvas.Background = [System.Windows.Media.Brushes]::White
$blueConv = New-Object System.Windows.Media.BrushConverter
$blueBrush = $blueConv.ConvertFromString("#0062AE")
$yellowBrush= $blueConv.ConvertFromString("#FFC627")
$p1 = New-Object System.Windows.Shapes.Polygon
$p1.Fill = $blueBrush
$p1.Points = [System.Windows.Media.PointCollection]::Parse("0,0 0,35.32 25.85,35.32 25.85,22.82 41.48,22.82 41.48,47.82 50.85,47.82 50.85,35.32 74.34,35.32 74.34,0 0,0")
$canvas.Children.Add($p1) | Out-Null
$p2 = New-Object System.Windows.Shapes.Polygon
$p2.Fill = $yellowBrush
$p2.Points = [System.Windows.Media.PointCollection]::Parse("0,38.45 28.98,38.45 28.98,25.95 38.35,25.95 38.35,50.95 53.98,50.95 53.98,38.45 74.34,38.45 74.34,74.34 0,74.34 0,38.45")
$canvas.Children.Add($p2) | Out-Null
$viewbox = New-Object System.Windows.Controls.Viewbox
$viewbox.Width = $Size
$viewbox.Height = $Size
$viewbox.Child = $canvas
$viewbox.Measure((New-Object System.Windows.Size($Size, $Size)))
$viewbox.Arrange((New-Object System.Windows.Rect(0, 0, $Size, $Size)))
$rtb = New-Object System.Windows.Media.Imaging.RenderTargetBitmap($Size, $Size, 96, 96, [System.Windows.Media.PixelFormats]::Pbgra32)
$rtb.Render($viewbox)
$encoder = New-Object System.Windows.Media.Imaging.PngBitmapEncoder
$encoder.Frames.Add([System.Windows.Media.Imaging.BitmapFrame]::Create($rtb))
$ms = New-Object System.IO.MemoryStream
$encoder.Save($ms)
$ms.Position = 0
$bmp = [System.Drawing.Bitmap]::FromStream($ms)
$hicon = $bmp.GetHicon()
$icon = [System.Drawing.Icon]::FromHandle($hicon)
$Global:AppIcon = $icon # drzime referenci po celou dobu behu, aby GC nezrusil handle
$dir = Split-Path $OutIcoPath
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
$fs = New-Object System.IO.FileStream($OutIcoPath, [System.IO.FileMode]::Create)
$icon.Save($fs)
$fs.Close()
$imgSource = [System.Windows.Interop.Imaging]::CreateBitmapSourceFromHIcon(
$icon.Handle, [System.Windows.Int32Rect]::Empty, [System.Windows.Media.Imaging.BitmapSizeOptions]::FromEmptyOptions())
$bmp.Dispose()
return $imgSource
}
try {
$window.Icon = New-LogoIcon -OutIcoPath $Global:IconPath
} catch {
# Pokud generovani ikony selze, aplikace normalne pobezi jen s vychozi ikonou
}
# Explicitni nastaveni ikony pres Win32 (WM_SETICON) - zajisti spravnou ikonu
# i na tlacitku hlavniho panelu, kde samotne Window.Icon nekdy nestaci.
Add-Type -Name Win32Icon -Namespace ITAdminTool -MemberDefinition @'
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
'@
$window.Add_Loaded({
try {
if ($Global:AppIcon) {
$hwnd = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle
$h = $Global:AppIcon.Handle
[ITAdminTool.Win32Icon]::SendMessage($hwnd, 0x0080, [IntPtr]0, $h) | Out-Null # ICON_SMALL
[ITAdminTool.Win32Icon]::SendMessage($hwnd, 0x0080, [IntPtr]1, $h) | Out-Null # ICON_BIG
}
} catch {}
})
# Namovane prvky
$StatusText = $window.FindName("StatusText")
$AppsPanel = $window.FindName("AppsPanel")
$BloatPanel = $window.FindName("BloatPanel")
$SettingsPanel= $window.FindName("SettingsPanel")
$DiagPanel = $window.FindName("DiagPanel")
$Global:MainTabControl = $window.FindName("MainTabControl")
$Global:LogBox = $window.FindName("LogBox")
$Global:OdtConfigBox = $window.FindName("OdtConfigBox")
$OdtVersionCombo = $window.FindName("OdtVersionCombo")
$OdtChannelCombo = $window.FindName("OdtChannelCombo")
$OdtLangCombo = $window.FindName("OdtLangCombo")
$OdtArchCombo = $window.FindName("OdtArchCombo")
$OdtExcludePanel = $window.FindName("OdtExcludePanel")
$OdtAutoActivate = $window.FindName("OdtAutoActivate")
$OdtRemoveMSI = $window.FindName("OdtRemoveMSI")
$OfficeFixPanel = $window.FindName("OfficeFixPanel")
function Set-Status($text) { $StatusText.Text = $text }
# --- Naplneni ODT vyberovych prvku ---------------------------------------
$OdtVersions = @(
@{ Name = "Microsoft 365 Apps for Enterprise (O365ProPlusRetail)"; Product = "O365ProPlusRetail"; Channel = "Current" }
@{ Name = "Microsoft 365 Apps for Business (O365BusinessRetail)"; Product = "O365BusinessRetail"; Channel = "Current" }
@{ Name = "Office LTSC 2021 (ProPlus2021Volume)"; Product = "ProPlus2021Volume"; Channel = "PerpetualVL2021" }
@{ Name = "Office LTSC 2024 (ProPlus2024Volume)"; Product = "ProPlus2024Volume"; Channel = "PerpetualVL2024" }
)
foreach ($v in $OdtVersions) {
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Content = $v.Name
$item.Tag = "$($v.Product)|$($v.Channel)"
$OdtVersionCombo.Items.Add($item) | Out-Null
}
$OdtVersionCombo.SelectedIndex = 0
foreach ($c in @("Current","MonthlyEnterprise","SemiAnnual","SemiAnnualPreview","PerpetualVL2021","PerpetualVL2024")) {
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Content = $c; $item.Tag = $c
$OdtChannelCombo.Items.Add($item) | Out-Null
}
$OdtChannelCombo.SelectedIndex = 0
foreach ($l in @(@{N="Čeština";I="cs-cz"},@{N="Angličtina (US)";I="en-us"},@{N="Slovenština";I="sk-sk"},@{N="Němčina";I="de-de"})) {
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Content = $l.N; $item.Tag = $l.I
$OdtLangCombo.Items.Add($item) | Out-Null
}
$OdtLangCombo.SelectedIndex = 0
foreach ($a in @(@{N="64-bit";I="64"},@{N="32-bit";I="32"})) {
$item = New-Object System.Windows.Controls.ComboBoxItem
$item.Content = $a.N; $item.Tag = $a.I
$OdtArchCombo.Items.Add($item) | Out-Null
}
$OdtArchCombo.SelectedIndex = 0
$Global:OdtExcludeCheckboxes = @{}
foreach ($app in @("Access","Publisher","Lync","OneDrive","OneNote","Outlook","Teams","Groove","Bing")) {
$cb = New-Object System.Windows.Controls.CheckBox
$cb.Content = $app
$cb.Width = 110
if ($app -in @("Access","Publisher","Lync")) { $cb.IsChecked = $true }
$OdtExcludePanel.Children.Add($cb) | Out-Null
$Global:OdtExcludeCheckboxes[$app] = $cb
}
function Build-OdtXml {
$verTag = $OdtVersionCombo.SelectedItem.Tag -split '\|'
$product = $verTag[0]
$channel = $OdtChannelCombo.SelectedItem.Tag
$lang = $OdtLangCombo.SelectedItem.Tag
$arch = $OdtArchCombo.SelectedItem.Tag
$excludes = ($Global:OdtExcludeCheckboxes.GetEnumerator() | Where-Object { $_.Value.IsChecked } | ForEach-Object {
" "
}) -join "`r`n"
$autoActivate = if ($OdtAutoActivate.IsChecked) { "`r`n " } else { "" }
$removeMsi = if ($OdtRemoveMSI.IsChecked) { "`r`n " } else { "" }
$Global:OdtConfigBox.Text = @"
$excludes
$autoActivate$removeMsi
"@
}
$OdtVersionCombo.Add_SelectionChanged({
$verTag = $OdtVersionCombo.SelectedItem.Tag -split '\|'
foreach ($item in $OdtChannelCombo.Items) { if ($item.Tag -eq $verTag[1]) { $OdtChannelCombo.SelectedItem = $item } }
Build-OdtXml
})
$OdtChannelCombo.Add_SelectionChanged({ Build-OdtXml })
$OdtLangCombo.Add_SelectionChanged({ Build-OdtXml })
$OdtArchCombo.Add_SelectionChanged({ Build-OdtXml })
$OdtAutoActivate.Add_Click({ Build-OdtXml })
$OdtRemoveMSI.Add_Click({ Build-OdtXml })
foreach ($cb in $Global:OdtExcludeCheckboxes.Values) { $cb.Add_Click({ Build-OdtXml }) }
Build-OdtXml
# ---------------------------------------------------------------------------
# 3) Definice dat - uprav si podle potreby
# ---------------------------------------------------------------------------
# Aplikace k instalaci: Nazev | winget ID
$AppList = @(
@{ Name = "Microsoft 365 Apps (Office)"; Id = "Microsoft.Office" }
@{ Name = "7-Zip"; Id = "7zip.7zip" }
@{ Name = "Google Chrome"; Id = "Google.Chrome" }
@{ Name = "Mozilla Firefox"; Id = "Mozilla.Firefox" }
@{ Name = "VLC media player"; Id = "VideoLAN.VLC" }
@{ Name = "Notepad++"; Id = "Notepad++.Notepad++" }
@{ Name = "Adobe Acrobat Reader"; Id = "Adobe.Acrobat.Reader.64-bit" }
@{ Name = "Microsoft Teams"; Id = "Microsoft.Teams" }
@{ Name = "Zoom"; Id = "Zoom.Zoom" }
@{ Name = "WinRAR"; Id = "RARLab.WinRAR" }
@{ Name = "Java Runtime"; Id = "Oracle.JavaRuntimeEnvironment" }
@{ Name = ".NET Desktop Runtime 8"; Id = "Microsoft.DotNet.DesktopRuntime.8" }
@{ Name = "Total Commander"; Id = "Ghisler.TotalCommander" }
@{ Name = "PDF24 Creator"; Id = "PDF24.PDF24Creator" }
@{ Name = "TeamViewer"; Id = "TeamViewer.TeamViewer" }
@{ Name = "Dell SupportAssist"; Id = "XPDCCXN26VNBT"; Source = "msstore" }
)
# Bloatware: Nazev | Appx nazev/wildcard
$BloatList = @(
@{ Name = "Xbox aplikace (vše)"; Id = "*Xbox*" }
@{ Name = "Solitaire Collection"; Id = "Microsoft.MicrosoftSolitaireCollection" }
@{ Name = "Candy Crush"; Id = "*CandyCrush*" }
@{ Name = "Skype (předinstalovaný)"; Id = "Microsoft.SkypeApp" }
@{ Name = "Get Help"; Id = "Microsoft.GetHelp" }
@{ Name = "Začínáme (Get Started)"; Id = "Microsoft.Getstarted" }
@{ Name = "Feedback Hub"; Id = "Microsoft.WindowsFeedbackHub" }
@{ Name = "Mixed Reality Portal"; Id = "Microsoft.MixedReality.Portal" }
@{ Name = "People"; Id = "Microsoft.People" }
@{ Name = "3D Viewer"; Id = "Microsoft.Microsoft3DViewer" }
@{ Name = "Mapy"; Id = "Microsoft.WindowsMaps" }
@{ Name = "Your Phone / Phone Link"; Id = "Microsoft.YourPhone" }
@{ Name = "Zune Music/Video"; Id = "*Zune*" }
@{ Name = "Počasí (Bing Weather)"; Id = "Microsoft.BingWeather" }
@{ Name = "Clipchamp"; Id = "Clipchamp.Clipchamp" }
@{ Name = "To Do"; Id = "Microsoft.Todos" }
)
$Global:AppCheckboxes = @{}
$Global:AppSourceMap = @{}
foreach ($app in $AppList) {
$cb = New-Object System.Windows.Controls.CheckBox
$cb.Content = $app.Name
$cb.Width = 260
$AppsPanel.Children.Add($cb) | Out-Null
$Global:AppCheckboxes[$app.Id] = $cb
$Global:AppSourceMap[$app.Id] = if ($app.Source) { $app.Source } else { "winget" }
}
$Global:BloatCheckboxes = @{}
foreach ($b in $BloatList) {
$cb = New-Object System.Windows.Controls.CheckBox
$cb.Content = $b.Name
$cb.Width = 260
$BloatPanel.Children.Add($cb) | Out-Null
$Global:BloatCheckboxes[$b.Id] = $cb
}
# ---------------------------------------------------------------------------
# 4) Systemova nastaveni - tlacitka s akcemi
# ---------------------------------------------------------------------------
function New-ActionButton($panel, $label, $color, $action) {
$btn = New-Object System.Windows.Controls.Button
$btn.Content = $label
$btn.Width = 280
$btn.Height = 40
if ($color) { $btn.Background = $color }
$btn.Add_Click({ & $action }.GetNewClosure())
$panel.Children.Add($btn) | Out-Null
}
New-ActionButton $SettingsPanel "Zobrazit přípony souborů" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" HideFileExt 0
Write-Log "Nastaveno: zobrazit přípony souborů"
}
New-ActionButton $SettingsPanel "Zobrazit skryté soubory" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" Hidden 1
Write-Log "Nastaveno: zobrazit skryté soubory"
}
New-ActionButton $SettingsPanel "Explorer -> otevírat na Tento počítač" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" LaunchTo 1
Write-Log "Nastaveno: Explorer otevírá Tento počítač"
}
New-ActionButton $SettingsPanel "Napájení: Vysoký výkon" $null {
powercfg /setactive SCHEME_MIN
Write-Log "Nastaven plán napájení: Vysoký výkon"
}
New-ActionButton $SettingsPanel "Nikdy nevypínat displej/spánek (AC)" $null {
powercfg /change monitor-timeout-ac 0
powercfg /change standby-timeout-ac 0
Write-Log "Nastaveno: bez uspávání při napájení ze sítě"
}
New-ActionButton $SettingsPanel "Vypnout návrhy v nabídce Start" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" SystemPaneSuggestionsEnabled 0
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" SilentInstalledAppsEnabled 0
Write-Log "Vypnuty návrhy/reklamy ve Start menu"
}
New-ActionButton $SettingsPanel "Skrýt ikonu Widgets z hlavního panelu" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" TaskbarDa 0
Write-Log "Skryta ikona Widgets"
}
New-ActionButton $SettingsPanel "Skrýt ikonu Chat/Teams z hlavního panelu" $null {
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" TaskbarMn 0
Write-Log "Skryta ikona Chat"
}
New-ActionButton $SettingsPanel "Vypnout rychlé spuštění (Fast Startup)" $null {
powercfg /hibernate off
Write-Log "Vypnuto rychlé spuštění / hibernace"
}
New-ActionButton $SettingsPanel "Povolit vzdálenou plochu (RDP)" $null {
Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server" fDenyTSConnections 0
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Write-Log "Povolena vzdálená plocha (RDP)"
}
New-ActionButton $SettingsPanel "Omezit telemetrii (Diagnostic data = Basic)" $null {
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection" AllowTelemetry 1 -ErrorAction SilentlyContinue
Write-Log "Omezena telemetrie na základní úroveň"
}
New-ActionButton $SettingsPanel "Vytvořit zástupce na ploše (s vlastní ikonou)" $null {
try {
if (-not (Test-Path $Global:IconPath)) { New-LogoIcon -OutIcoPath $Global:IconPath | Out-Null }
$shell = New-Object -ComObject WScript.Shell
$shortcutPath = Join-Path ([Environment]::GetFolderPath("Desktop")) "IT Admin Tool.lnk"
$shortcut = $shell.CreateShortcut($shortcutPath)
$shortcut.TargetPath = "powershell.exe"
$shortcut.Arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
$shortcut.IconLocation = $Global:IconPath
$shortcut.WorkingDirectory = Split-Path $PSCommandPath
$shortcut.Save()
Write-Log "Zástupce s vlastní ikonou vytvořen na ploše: $shortcutPath"
} catch {
Write-Log "Nepodařilo se vytvořit zástupce: $_" "ERROR"
}
}
New-ActionButton $SettingsPanel "Nastavit časové pásmo automaticky" $null {
Set-Service tzautoupdate -StartupType Automatic
Start-Service tzautoupdate
Write-Log "Zapnuto automatické časové pásmo"
}
# ---------------------------------------------------------------------------
# 5) Diagnostika - bezi na pozadi (job), vystup jde primo do zalozky Log
# ---------------------------------------------------------------------------
function Invoke-DiagCommand {
param([string]$Title, [scriptblock]$Script)
Write-Log "=== Spouštím: $Title ==="
Set-Status "Spouštím: $Title (výstup viz záložka Log)..."
if ($Global:MainTabControl) { $Global:MainTabControl.SelectedIndex = 5 } # prepnout na zalozku Log
Run-Background -JobName $Title -Script $Script
}
New-ActionButton $DiagPanel "SFC - kontrola systémových souborů" $null {
Invoke-DiagCommand "SFC scannow" { sfc /scannow 2>&1 | Out-String }
}
New-ActionButton $DiagPanel "CHKDSK - kontrola disku (jen sken)" $null {
Invoke-DiagCommand "CHKDSK /scan" { chkdsk C: /scan 2>&1 | Out-String }
}
New-ActionButton $DiagPanel "DISM - CheckHealth" $null {
Invoke-DiagCommand "DISM CheckHealth" { DISM /Online /Cleanup-Image /CheckHealth 2>&1 | Out-String }
}
New-ActionButton $DiagPanel "DISM - ScanHealth" $null {
Invoke-DiagCommand "DISM ScanHealth" { DISM /Online /Cleanup-Image /ScanHealth 2>&1 | Out-String }
}
New-ActionButton $DiagPanel "DISM - RestoreHealth" $null {
Invoke-DiagCommand "DISM RestoreHealth" { DISM /Online /Cleanup-Image /RestoreHealth 2>&1 | Out-String }
}
New-ActionButton $DiagPanel "Stav disků a volumů" $null {
Invoke-DiagCommand "Get-PhysicalDisk / Get-Volume" {
Get-PhysicalDisk | Format-Table -AutoSize | Out-String
Get-Volume | Format-Table -AutoSize | Out-String
}
}
New-ActionButton $DiagPanel "Reset sítě (DNS + Winsock)" $null {
Invoke-DiagCommand "Network reset" {
ipconfig /flushdns 2>&1 | Out-String
netsh winsock reset 2>&1 | Out-String
netsh int ip reset 2>&1 | Out-String
}
}
New-ActionButton $DiagPanel "Chyby v Event Logu (24h)" $null {
Invoke-DiagCommand "Event log errors" {
Get-WinEvent -FilterHashtable @{LogName='System';Level=2;StartTime=(Get-Date).AddHours(-24)} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, Message | Format-List | Out-String
}
}
New-ActionButton $DiagPanel "Test paměti RAM (restart!)" $null {
Start-Process mdsched.exe
Write-Log "Spuštěn nástroj pro diagnostiku paměti (vyžaduje restart)"
}
New-ActionButton $DiagPanel "Report volného místa na discích" $null {
Invoke-DiagCommand "Volné místo" {
Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size | Format-Table -AutoSize | Out-String
}
}
New-ActionButton $DiagPanel "Seznam nainstalovaných ovladačů" $null {
Invoke-DiagCommand "Ovladače" {
Get-WindowsDriver -Online | Select-Object OriginalFileName, ClassName, ProviderName, Date | Format-Table -AutoSize | Out-String
}
}
New-ActionButton $DiagPanel "Historie Windows Update" $null {
Invoke-DiagCommand "Windows Update historie" {
Get-HotFix | Sort-Object InstalledOn -Descending | Format-Table -AutoSize | Out-String
}
}
# ---------------------------------------------------------------------------
# 6) Handlery hlavnich akci (Instalace / Bloatware / Log)
# ---------------------------------------------------------------------------
function Run-Background {
param([scriptblock]$Script, [string]$JobName, [object[]]$ArgList = @())
$job = Start-Job -Name $JobName -ScriptBlock $Script -ArgumentList $ArgList
$timer = New-Object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]::FromSeconds(1)
$timer.Add_Tick({
if ($job.State -eq 'Running') {
Receive-Job $job | ForEach-Object { Write-Log $_ }
} else {
Receive-Job $job | ForEach-Object { Write-Log $_ }
Write-Log "Úloha '$($job.Name)' dokončena (stav: $($job.State))."
Set-Status "Připraveno."
Remove-Job $job -Force
$timer.Stop()
}
}.GetNewClosure())
$timer.Start()
}
$window.FindName("BtnSelectAllApps").Add_Click({ $Global:AppCheckboxes.Values | ForEach-Object { $_.IsChecked = $true } })
$window.FindName("BtnSelectNoneApps").Add_Click({ $Global:AppCheckboxes.Values | ForEach-Object { $_.IsChecked = $false } })
$window.FindName("BtnInstallApps").Add_Click({
$selected = @($Global:AppCheckboxes.GetEnumerator() | Where-Object { $_.Value.IsChecked } | ForEach-Object { $_.Key })
if (-not $selected -or $selected.Count -eq 0) { Write-Log "Nebyly vybrány žádné aplikace." "WARN"; return }
$sourceMap = $Global:AppSourceMap
Set-Status "Instalace aplikací na pozadí..."
Write-Log "Zahajuji instalaci: $($selected -join ', ')"
Run-Background -JobName "Install" -ArgList @($selected, $sourceMap) -Script {
param($ids, $sources)
# msstore zdroj potrebuje (alespon jednou na stroji) explicitne "prijmout"
# podminky - bez pripojene konzole (job na pozadi) samotne --accept-source-agreements
# u instalace nekdy nestaci, takze to udelame samostatnym krokem predem.
if (($sources.Values) -contains "msstore") {
"Potvrzuji podmínky zdroje Microsoft Store (msstore) ..."
winget source update --name msstore --accept-source-agreements 2>&1 | Out-String
}
foreach ($id in $ids) {
$src = $sources[$id]
"Instaluji $id (zdroj: $src) ..."
winget install --id $id -e --source $src --silent --accept-package-agreements --accept-source-agreements 2>&1 | Out-String
}
}
})
$window.FindName("BtnSelectAllBloat").Add_Click({ $Global:BloatCheckboxes.Values | ForEach-Object { $_.IsChecked = $true } })
$window.FindName("BtnSelectNoneBloat").Add_Click({ $Global:BloatCheckboxes.Values | ForEach-Object { $_.IsChecked = $false } })
$window.FindName("BtnRemoveBloat").Add_Click({
$selected = $Global:BloatCheckboxes.GetEnumerator() | Where-Object { $_.Value.IsChecked } | ForEach-Object { $_.Key }
if (-not $selected) { Write-Log "Nebyl vybrán žádný bloatware." "WARN"; return }
Write-Log "Odstraňuji: $($selected -join ', ')"
Set-Status "Odebírám bloatware..."
foreach ($pattern in $selected) {
Get-AppxPackage -AllUsers -Name $pattern -ErrorAction SilentlyContinue | Remove-AppxPackage -AllUsers -ErrorAction SilentlyContinue
Get-AppxProvisionedPackage -Online | Where-Object DisplayName -like $pattern | Remove-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue | Out-Null
Write-Log "Odebráno (pokud existovalo): $pattern"
}
Set-Status "Připraveno."
})
$window.FindName("BtnRemoveOneDrive").Add_Click({
Write-Log "Odinstalace OneDrive..."
Stop-Process -Name OneDrive -ErrorAction SilentlyContinue
$oneDriveSetup = "$env:SYSTEMROOT\SysWOW64\OneDriveSetup.exe"
if (-not (Test-Path $oneDriveSetup)) { $oneDriveSetup = "$env:SYSTEMROOT\System32\OneDriveSetup.exe" }
if (Test-Path $oneDriveSetup) {
Start-Process $oneDriveSetup -ArgumentList "/uninstall" -Wait
Write-Log "OneDrive odinstalován."
} else {
Write-Log "OneDriveSetup.exe nenalezen." "WARN"
}
})
# --- Oprava Office - znama reseni z internetu ----------------------------
function Invoke-OfficeFix {
param([string]$ExePath, [string[]]$Arguments, [string]$Label)
if (-not (Test-Path $ExePath)) {
Write-Log "Spustitelný soubor nenalezen: $ExePath (tato oprava zřejmě neodpovídá nainstalované verzi Office)." "WARN"
Set-Status "Soubor pro '$Label' nenalezen."
return
}
Write-Log "Spouštím: $Label -> `"$ExePath`" $($Arguments -join ' ')"
try {
Start-Process -FilePath $ExePath -ArgumentList $Arguments
Write-Log "$Label - spuštěno (aktualizace/oprava běží na pozadí přes Click-to-Run)."
Set-Status "$Label - spuštěno."
} catch {
Write-Log "Chyba při spouštění '$Label': $_" "ERROR"
}
}
New-ActionButton $OfficeFixPanel "Zjistit nainstalovanou verzi Office" $null {
$reg = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" -ErrorAction SilentlyContinue
if ($reg) {
Write-Log "Nainstalovaná verze Office (Click-to-Run): $($reg.VersionToReport)"
} else {
Write-Log "Click-to-Run registry nenalezen - Office je pravděpodobně MSI instalace (např. 2016)." "WARN"
}
}
New-ActionButton $OfficeFixPanel "Office 2016 - oprava (Oarpmany)" $null {
Invoke-OfficeFix -Label "Office 2016 Oarpmany fix" `
-ExePath "C:\Program Files\Common Files\Microsoft Shared\OFFICE16\Oarpmany.exe" `
-Arguments @('/removereleaseinpatch','{90160000-0012-0000-1000-0000000FF1CE}','{27882596-A8ED-4382-9C71-6CD2DD19F732}','1033','0')
}
New-ActionButton $OfficeFixPanel "Office 2019 -> 16.0.10417.20197" $null {
Invoke-OfficeFix -Label "Office 2019 update" `
-ExePath "C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe" `
-Arguments @('/update','user','updatetoversion=16.0.10417.20197')
}
New-ActionButton $OfficeFixPanel "Office 2021 / 2024 -> 16.0.20326.20132" $null {
Invoke-OfficeFix -Label "Office 2021/2024 update" `
-ExePath "C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe" `
-Arguments @('/update','user','updatetoversion=16.0.20326.20132')
}
New-ActionButton $OfficeFixPanel "Office 2024 LTSC -> 16.0.17932.20910" $null {
Invoke-OfficeFix -Label "Office 2024 LTSC update" `
-ExePath "C:\Program Files\Common Files\microsoft shared\ClickToRun\officec2rclient.exe" `
-Arguments @('/update','user','updatetoversion=16.0.17932.20910')
}
$window.FindName("BtnOfficeCustomUpdate").Add_Click({
$ver = $window.FindName("OfficeCustomVersionBox").Text.Trim()
if ($ver -notmatch '^\d+\.\d+\.\d+\.\d+$') {
Write-Log "Neplatný formát verze. Očekávaný formát: 16.0.XXXXX.XXXXX" "WARN"
return
}
Invoke-OfficeFix -Label "Vlastní aktualizace na $ver" `
-ExePath "C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe" `
-Arguments @('/update','user',"updatetoversion=$ver")
})
# --- Office Deployment Tool (ODT) ---------------------------------------
function Get-LatestOdtUrl {
# Microsoft neposkytuje stabilni primy odkaz - ziskame ho ze stranky Download Center
$page = Invoke-WebRequest -Uri "https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117" -UseBasicParsing
$link = ($page.Links | Where-Object { $_.href -match "officedeploymenttool.*\.exe$" } | Select-Object -First 1).href
if (-not $link) { throw "Nepodařilo se na stránce Microsoftu najít odkaz na aktuální ODT." }
return $link
}
$window.FindName("BtnOdtDownload").Add_Click({
try {
if (-not (Test-Path $Global:OdtDir)) { New-Item -ItemType Directory -Path $Global:OdtDir -Force | Out-Null }
Write-Log "Hledám aktuální verzi Office Deployment Tool..."
Set-Status "Stahuji ODT..."
$url = Get-LatestOdtUrl
$exePath = Join-Path $Global:OdtDir "odtsetup.exe"
Invoke-WebRequest -Uri $url -OutFile $exePath -UseBasicParsing
Write-Log "Staženo: $url"
Start-Process -FilePath $exePath -ArgumentList "/quiet /extract:`"$Global:OdtDir`"" -Wait
Write-Log "ODT rozbaleno do $Global:OdtDir (setup.exe je připraveno k použití)."
Set-Status "ODT připraveno."
} catch {
Write-Log "Chyba při stahování ODT: $_" "ERROR"
Set-Status "Chyba při stahování ODT."
}
})
$window.FindName("BtnOdtInstall").Add_Click({
Build-OdtXml
$setupExe = Join-Path $Global:OdtDir "setup.exe"
if (-not (Test-Path $setupExe)) {
Write-Log "setup.exe nenalezen - nejdřív klikni na '1) Stáhnout a rozbalit ODT'." "WARN"
Set-Status "ODT není stažené."
return
}
$configPath = Join-Path $Global:OdtDir "configuration.xml"
Set-Content -Path $configPath -Value $Global:OdtConfigBox.Text -Encoding UTF8
Write-Log "Konfigurace uložena do $configPath"
Write-Log "Spouštím instalaci Office přes ODT (běží na pozadí, může trvat i desítky minut)..."
Set-Status "Instaluji Office (ODT) na pozadí..."
Run-Background -JobName "ODT" -ArgList @($setupExe, $configPath) -Script {
param($exe, $cfg)
"Spouštím: $exe /configure `"$cfg`""
& $exe /configure $cfg 2>&1 | Out-String
"Instalace Office (ODT) dokončena."
}
})
$window.FindName("BtnOdtSaveXml").Add_Click({
Build-OdtXml
$dlg = New-Object Microsoft.Win32.SaveFileDialog
$dlg.FileName = "configuration.xml"
$dlg.Filter = "XML soubor (*.xml)|*.xml"
if ($dlg.ShowDialog()) {
Set-Content -Path $dlg.FileName -Value $Global:OdtConfigBox.Text -Encoding UTF8
Write-Log "Konfigurace ODT uložena do: $($dlg.FileName)"
}
})
$window.FindName("BtnSaveLog").Add_Click({
$dlg = New-Object Microsoft.Win32.SaveFileDialog
$dlg.FileName = "ITAdminTool_log.txt"
$dlg.Filter = "Textový soubor (*.txt)|*.txt"
if ($dlg.ShowDialog()) {
Copy-Item $Global:LogPath $dlg.FileName -Force
Write-Log "Log uložen do: $($dlg.FileName)"
}
})
$window.FindName("BtnClearLog").Add_Click({ $Global:LogBox.Clear() })
Write-Log "IT Admin Tool spuštěn. Log: $Global:LogPath"
$window.ShowDialog() | Out-Null