Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing [System.Windows.Forms.Application]::EnableVisualStyles() # ==== Config SQL IVANTI ==== $script:IvantiSqlInstance = "serveurIVANTI.domain.lan" $script:IvantiSqlDatabase = "LDMS123" # ----------------------------- # Form setup (compact & pro) # ----------------------------- $form = New-Object System.Windows.Forms.Form $form.Text = 'Ops Maintenance Console' $form.Size = New-Object System.Drawing.Size(465,580) $form.StartPosition= 'CenterScreen' $form.FormBorderStyle = 'FixedDialog' $form.MaximizeBox = $false $form.MinimizeBox = $false $form.Font = New-Object System.Drawing.Font('Segoe UI', 9) # Status strip (status text + progress) $statusStrip = New-Object System.Windows.Forms.StatusStrip $statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel $prog = New-Object System.Windows.Forms.ToolStripProgressBar $statusLabel.Spring = $true $prog.Minimum = 0; $prog.Maximum = 1; $prog.Step = 1; $prog.Value = 0 [void]$statusStrip.Items.Add($statusLabel) [void]$statusStrip.Items.Add($prog) $form.Controls.Add($statusStrip) function Set-Status([string]$text){ $statusLabel.Text = $text } # ----------------------------- # GroupBox: Task # ----------------------------- $gbTask = New-Object System.Windows.Forms.GroupBox $gbTask.Text = 'Task' $gbTask.Location = New-Object System.Drawing.Point(10,8) $gbTask.Size = New-Object System.Drawing.Size(445,80) $form.Controls.Add($gbTask) $lblSelect = New-Object System.Windows.Forms.Label $lblSelect.Text = 'Select:' $lblSelect.Location = New-Object System.Drawing.Point(12,30) $lblSelect.AutoSize = $true $gbTask.Controls.Add($lblSelect) $comboBox = New-Object System.Windows.Forms.ComboBox $comboBox.Location = New-Object System.Drawing.Point(65,27) $comboBox.Size = New-Object System.Drawing.Size(230,24) $comboBox.DropDownStyle = 'DropDownList' $comboBox.Items.Add('IVANTI Core') | Out-Null $comboBox.Items.Add('IVANTI Console') | Out-Null $comboBox.Items.Add('WSUS') | Out-Null $gbTask.Controls.Add($comboBox) $btnRun = New-Object System.Windows.Forms.Button $btnRun.Text = 'Run' $btnRun.Location = New-Object System.Drawing.Point(310,26) $btnRun.Size = New-Object System.Drawing.Size(110,26) $gbTask.Controls.Add($btnRun) $form.AcceptButton = $btnRun # ----------------------------- # GroupBox: Credentials # ----------------------------- $gbCreds = New-Object System.Windows.Forms.GroupBox $gbCreds.Text = 'Credentials (IVANTI Core)' $gbCreds.Location = New-Object System.Drawing.Point(10,95) $gbCreds.Size = New-Object System.Drawing.Size(445,90) $form.Controls.Add($gbCreds) $labelUser = New-Object System.Windows.Forms.Label $labelUser.Text = 'Username:' $labelUser.Location = New-Object System.Drawing.Point(12,27) $labelUser.AutoSize = $true $gbCreds.Controls.Add($labelUser) $textBoxNom = New-Object System.Windows.Forms.TextBox $textBoxNom.Location = New-Object System.Drawing.Point(90,24) $textBoxNom.Size = New-Object System.Drawing.Size(150,24) $textBoxNom.Enabled = $false $gbCreds.Controls.Add($textBoxNom) $labelPwd = New-Object System.Windows.Forms.Label $labelPwd.Text = 'Password:' $labelPwd.Location = New-Object System.Drawing.Point(12,57) $labelPwd.AutoSize = $true $gbCreds.Controls.Add($labelPwd) $textBoxPassword = New-Object System.Windows.Forms.TextBox $textBoxPassword.Location = New-Object System.Drawing.Point(90,54) $textBoxPassword.Size = New-Object System.Drawing.Size(150,24) $textBoxPassword.UseSystemPasswordChar = $true $textBoxPassword.Enabled = $false $gbCreds.Controls.Add($textBoxPassword) # ----------------------------- # GroupBox: Output # ----------------------------- $gbOut = New-Object System.Windows.Forms.GroupBox $gbOut.Text = 'Output' $gbOut.Location = New-Object System.Drawing.Point(10,190) $gbOut.Size = New-Object System.Drawing.Size(445,330) $form.Controls.Add($gbOut) $outputBox = New-Object System.Windows.Forms.RichTextBox $outputBox.Location = New-Object System.Drawing.Point(12,22) $outputBox.Size = New-Object System.Drawing.Size(420,270) $outputBox.Font = New-Object System.Drawing.Font('Consolas', 9) $outputBox.ReadOnly = $true $outputBox.WordWrap = $false $gbOut.Controls.Add($outputBox) $btnClear = New-Object System.Windows.Forms.Button $btnClear.Text= 'Clear' $btnClear.Location = New-Object System.Drawing.Point(322,295) $btnClear.Size = New-Object System.Drawing.Size(110,26) $gbOut.Controls.Add($btnClear) # Helper: unified output function Append-Output([string]$text){ foreach($line in ($text -split "(`r`n|`n|`r)")){ if ([string]::IsNullOrWhiteSpace($line)) { continue } $color = [System.Drawing.Color]::Black if ($line -match '\bOK\b') { $color = [System.Drawing.Color]::ForestGreen } elseif ($line -match '\bKO\b') { $color = [System.Drawing.Color]::Crimson } $outputBox.SelectionStart = $outputBox.TextLength $outputBox.SelectionLength = 0 $outputBox.SelectionColor = $color $outputBox.AppendText($line + [Environment]::NewLine) $outputBox.SelectionColor = $outputBox.ForeColor } $outputBox.ScrollToCaret() } # ----------------------------- # Logging helper # ----------------------------- function Write-Log { [CmdletBinding()] param( [Parameter(Mandatory=$True)][string]$Message, [Parameter(Mandatory=$False)] [string]$Path = $(if ($MyInvocation.MyCommand.Path) { $MyInvocation.MyCommand.Path -replace '\.ps1$', '.log' } else { "C:\Windows\Temp\exploitbox.log" }) ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" "$timestamp : $script:SelectedOption : $Message" | Out-File -FilePath $Path -Append } # ----------------------------- # Functions (business) # ----------------------------- function Start-WSUS-Maintenance { Write-Host "[STEP] Start-WSUS-Maintenance" Set-Status "WSUS maintenance..." $Aff = "" $FileSQL = Join-Path $PSScriptRoot "WSUSMaintenance.sql" Write-Log -Message "----- Start-WSUS-Maintenance" $Instance = "\\.\pipe\MICROSOFT##WID\tsql\query" $Bdd = "SUSDB" try { Import-Module SqlServer -ErrorAction Stop Invoke-Sqlcmd -ServerInstance $Instance -Database $Bdd -InputFile $FileSQL -ErrorAction Stop -Verbose $Aff = "Maintenance OK" Write-Log -Message "WSUS maintenance OK" Write-Host "[DONE] WSUS maintenance OK" } catch { $Aff = "KO : $($_.Exception.Message)" Write-Log -Message "WSUS maintenance KO ($($_.Exception.Message))" Write-Host "[FAIL] WSUS maintenance KO $($_.Exception.Message)" } return ($Aff + "`r`n") } function Start-IVANTI-Maintenance { Write-Host "[STEP] Start-IVANTI-Maintenance" Set-Status "IVANTI SQL maintenance..." $Aff = "" $FileSQL = Join-Path $PSScriptRoot "SQLMaintenance2022.sql" Write-Log -Message "----- Start-IVANTI-Maintenance" $username = $textBoxNom.Text $password = $textBoxPassword.Text try { Import-Module SqlServer -ErrorAction Stop Invoke-Sqlcmd -ServerInstance $script:IvantiSqlInstance ` -Database $script:IvantiSqlDatabase ` -Username $username ` -Password $password ` -InputFile $FileSQL ` -ErrorAction Stop -Verbose $Aff = "Maintenance OK" Write-Log -Message "IVANTI SQL maintenance OK" Write-Host "[DONE] IVANTI SQL maintenance OK" } catch { $Aff = "KO : $($_.Exception.Message)" Write-Log -Message "IVANTI SQL maintenance KO ($($_.Exception.Message))" Write-Host "[FAIL] IVANTI SQL maintenance KO $($_.Exception.Message)" } return ($Aff + "`r`n") } function Get-RebootStatus { Write-Host "[STEP] Get-RebootStatus" Set-Status "Checking reboot status..." $Aff = "" Write-Log -Message "----- Get-RebootStatus" $rebootRequired = $false $rebootPaths = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' ) foreach ($p in $rebootPaths) { if (Test-Path $p) { $rebootRequired = $true; break } } if ($rebootRequired) { $Aff = "Reboot Needed (KO)"; Write-Log -Message $Aff; Write-Host "[INFO] Reboot Needed" } else { $Aff = "No Reboot Needed (OK)"; Write-Log -Message $Aff; Write-Host "[INFO] No Reboot Needed" } return ($Aff + "`r`n") } function Cleanup-IIS { Write-Host "[STEP] Cleanup-IIS" Set-Status "Cleaning IIS logs..." $Aff = ""; Write-Log -Message "----- Cleanup-IIS" $LogPath = "C:\inetpub\logs\LogFiles\W3SVC1" if (-not (Test-Path $LogPath)) { $Aff = "PurgeIIS : OK (no log folder)`r`n" Write-Log -Message "PurgeIIS : OK (no log folder)" Write-Host "[DONE] Cleanup-IIS OK (no folder)" return $Aff } $maxDaystoKeep = -30 $old = Get-ChildItem -Path $LogPath -File -Filter *.log | Where-Object LastWriteTime -lt ((Get-Date).AddDays($maxDaystoKeep)) if ($old.Count -gt 0){ foreach ($i in $old){ Remove-Item $i.FullName -Force -Verbose } } $old = Get-ChildItem -Path $LogPath -File -Filter *.log | Where-Object LastWriteTime -lt ((Get-Date).AddDays($maxDaystoKeep)) if ($old.Count -gt 0){ $Aff = "PurgeIIS : KO"; Write-Log -Message $Aff; Write-Host "[FAIL] Cleanup-IIS KO" } else { $Aff = "PurgeIIS : OK"; Write-Log -Message $Aff; Write-Host "[DONE] Cleanup-IIS OK" } return ($Aff + "`r`n") } function Check-ldscan { Write-Host "[STEP] Check-ldscan" Set-Status "Checking ldscan folder..." $Aff = ""; Write-Log -Message "----- Check-ldscan" $path = "C:\Program Files\LANDesk\ManagementSuite\ldscan" $count = Get-ChildItem $path -File | Measure-Object | ForEach-Object { $_.Count } if ($count -gt 200) { $Aff = "Countldscan : KO"; Write-Log -Message $Aff; Write-Host "[FAIL] ldscan KO ($count files)" } else { $count = Get-ChildItem $path -Recurse -File | Measure-Object | ForEach-Object { $_.Count } if ($count -gt 200) { $Aff = "Countldscan : WARNING"; Write-Log -Message $Aff; Write-Host "[WARN] ldscan WARNING ($count files)" } else { $Aff = "Countldscan : OK"; Write-Log -Message $Aff; Write-Host "[DONE] ldscan OK ($count files)" } } return ($Aff + "`r`n") } # PSWindowsUpdate function Install-WindowsUpdate { Write-Host "[STEP] Install-WindowsUpdate (PSWindowsUpdate)" Set-Status "Installing Windows Updates..." $Aff = ""; Write-Log -Message "----- Install-WindowsUpdate (PSWindowsUpdate)" try { if (-not (Get-Module -ListAvailable -Name PSWindowsUpdate)) { throw "Module PSWindowsUpdate not found on this system." } Import-Module PSWindowsUpdate -ErrorAction Stop # Remove -WindowsUpdate to honor WSUS policy $updates = PSWindowsUpdate\Install-WindowsUpdate -WindowsUpdate -AcceptAll -IgnoreReboot -ErrorAction Stop if ($updates) { $Aff += ($updates | Select-Object KB, Title, Result | Format-Table -AutoSize | Out-String) $updates | ForEach-Object { Write-Log -Message ("{0} {1} -> {2}" -f $_.KB, $_.Title, $_.Result) } | Out-Null Write-Host "[DONE] Windows Update processed (OK)" } else { $Aff += "No applicable updates (OK).`r`n"; Write-Log -Message "No applicable updates."; Write-Host "[INFO] No applicable updates" } } catch { $Aff += "PSWindowsUpdate failed (KO): $($_.Exception.Message)`r`n" Write-Log -Message "PSWindowsUpdate failed (KO): $($_.Exception.Message)" Write-Host "[FAIL] Windows Update failed (KO): $($_.Exception.Message)" } return $Aff } function Get-WSUS-PatchInGroups { Write-Host "[STEP] Get-WSUS-PatchInGroups" Set-Status "Building WSUS report..." $Aff = ""; Write-Log -Message "----- Get-WSUS-PatchInGroups" $MaxDaysReport = 35; $ListeKB=@() [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null $wsusServer = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer() $updates = $wsusServer.GetUpdates() $targetGroups = $wsusServer.GetComputerTargetGroups() foreach ($u in $updates){ $approvals = $u.GetUpdateApprovals() foreach ($ap in $approvals){ foreach ($tg in $targetGroups){ if ($tg.Id -eq $ap.ComputerTargetGroupId) { $days = (New-TimeSpan -Start $ap.goLiveTime -End (Get-Date)).Days $title = $u.Title; $group = $tg.Name if ($days -lt $MaxDaysReport) { $match = 0 if (($title -match "Windows 10") -and ($title -match "1809") -and ($title -match " x64")) { $match = 1 } if ($match -eq 1) { $ListeKB += [PSCustomObject]@{ KBTitre=$title; KBGroup=$group; KBChang=$days } } } } } } } $ListeKB = $ListeKB | Sort-Object KBTitre, KBChang $maxTitle=120; $maxGroup=25; $maxDays=5; $prev="####" foreach ($KB in $ListeKB) { if ($KB.KBTitre -ne $prev) { $Aff += "`r`n"; $prev = $KB.KBTitre } $line = "{0,-$maxTitle} | {1,-$maxGroup} | {2,-$maxDays}" -f $KB.KBTitre, $KB.KBGroup, $KB.KBChang $Aff += "`r`n$line"; Write-Log -Message $line } Write-Host "[DONE] WSUS patch listing generated" return ($Aff + "`r`n") } # ----------------------------- # Admin check # ----------------------------- if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process PowerShell -ArgumentList "-File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs exit } # ----------------------------- # UI events # ----------------------------- $comboBox.Add_SelectedIndexChanged({ $script:SelectedOption = $comboBox.SelectedItem if ($comboBox.SelectedItem -eq 'IVANTI Core') { $textBoxNom.Enabled = $true; $textBoxPassword.Enabled = $true; $textBoxNom.Text = "admindb" $gbCreds.Enabled = $true } else { $textBoxNom.Enabled = $false; $textBoxPassword.Enabled = $false; $gbCreds.Enabled = $false } }) $btnClear.Add_Click({ $outputBox.Clear(); Set-Status "Output cleared." }) $btnRun.Add_Click({ $selectedOption = $comboBox.SelectedItem $script:SelectedOption = $selectedOption if (-not $selectedOption) { [System.Windows.Forms.MessageBox]::Show("Please select a task.", "Info", 'OK', 'Information') | Out-Null; return } # progress config switch ($selectedOption) { 'IVANTI Core' { $prog.Maximum = 6 } 'IVANTI Console' { $prog.Maximum = 2 } 'WSUS' { $prog.Maximum = 4 } } $prog.Value = 0 if ($selectedOption -eq "IVANTI Core") { Set-Status "IVANTI Core running..." Append-Output "----- Check ldscan"; Append-Output (Check-ldscan); $prog.PerformStep() Append-Output "----- Clean IIS Log"; Append-Output (Cleanup-IIS); $prog.PerformStep() Append-Output "----- Windows Update"; Append-Output (Install-WindowsUpdate); $prog.PerformStep() Append-Output "----- SQL maintenance"; Append-Output (Start-IVANTI-Maintenance);$prog.PerformStep() Append-Output "----- Get Reboot"; Append-Output (Get-RebootStatus); $prog.PerformStep() Set-Status "IVANTI Core: done." } if ($selectedOption -eq "IVANTI Console") { Set-Status "IVANTI Console running..." Append-Output "----- Windows Update"; Append-Output (Install-WindowsUpdate); $prog.PerformStep() Append-Output "----- Get Reboot"; Append-Output (Get-RebootStatus); $prog.PerformStep() Set-Status "IVANTI Console: done." } if ($selectedOption -eq "WSUS") { Set-Status "WSUS tasks running..." Append-Output "----- Windows Update"; Append-Output (Install-WindowsUpdate); $prog.PerformStep() Append-Output "----- Get Reboot"; Append-Output (Get-RebootStatus); $prog.PerformStep() Append-Output "----- SQL Maintenance"; Append-Output (Start-WSUS-Maintenance); $prog.PerformStep() Append-Output "----- Deploy"; Append-Output (Get-WSUS-PatchInGroups); $prog.PerformStep() Set-Status "WSUS: done." } }) # ----------------------------- # Run UI # ----------------------------- $form.ShowDialog() | Out-Null