Thursday, February 12, 2015

Powershell WSUS Client Front End

This is a script that I wrote which uses the WSUS APIs in windows (COM) and presents the user with a GUI to select and install updates.  I was very happy to learn how to use a listview .NET control in this exercise.


Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

#####################
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'

function Show-Console {
   $consolePtr = [Console.Window]::GetConsoleWindow()
  #5 show
 [Console.Window]::ShowWindow($consolePtr, 5)
}

function Hide-Console {
    $consolePtr = [Console.Window]::GetConsoleWindow()
  #0 hide
 [Console.Window]::ShowWindow($consolePtr, 0)
}
#####################
#uncomment next line to hide console window to make things prettier if run from SCCM application/package for instance
#Hide-Console
Start-Process -FilePath "wuauclt.exe" -ArgumentList "/a /resetauthorization /detectnow"

#Alternate detection than wuauclt, not sure if it does the resetauthorization switch
#$AutoUpdate = New-Object -ComObject Microsoft.Update.AutoUpdate
#$AutoUpdate.DetectNow()

$UpdateCollection = New-Object -ComObject "Microsoft.Update.UpdateColl"
$Searcher = New-Object -ComObject "Microsoft.Update.Searcher"
$Session = New-Object -ComObject "Microsoft.Update.Session"
$Downloader = $Session.CreateUpdateDownloader()
$Installer = New-Object -ComObject "Microsoft.Update.Installer"

#May be necessary to specify other parameters to use a WSUS server
#$Searcher.ServerSelection = 0 #Default
#$Searcher.Online = $False

$LoadingForm = New-Object System.Windows.Forms.Form
$LoadingForm.Size = [System.Drawing.Point]"300,300"
$LoadingForm.FormBorderStyle = "None"
$LoadingForm.TopMost = $true
$LoadingForm.BackColor = [System.Drawing.Color]::PaleTurquoise
$LoadingForm.StartPosition = "CenterScreen"
$LoadingLabel = New-Object System.Windows.Forms.Label
$LoadingLabel.Text = "Loading updates, please wait..."
$LoadingLabel.Location = [System.Drawing.Point]"25,100"
$LoadingLabel.Font = "Calibri, 25"
$LoadingLabel.Size = [System.Drawing.Point]"300,200"
$LoadingForm.Controls.Add($LoadingLabel)
$LoadingForm.Show()

#Allow update scan time to work
Start-Sleep -Seconds 30

$NotInstalled = $Searcher.Search("IsInstalled=0")

$LoadingForm.Close()
#>
########Begin Form
Function New-Button($Text,$X,$Y,$ScriptBlock){
    $Button = New-Object System.Windows.Forms.Button
    $Button.Size = [System.Drawing.Point]"200,50"
    $Button.Location = [System.Drawing.Point]"$x,$y"
    $Button.Text = $Text
    $Button.Add_Click($ScriptBlock)
    $Button
}

########Begin Form
   
$Form = New-Object System.Windows.Forms.Form
$Form.Size = [System.Drawing.Point]"800,600"
$Form.StartPosition = "CenterScreen"
$Form.FormBorderStyle = "FixedSingle"
$Form.TopMost = $true
$Form.MinimizeBox = $false
$Form.MaximizeBox = $false

$ListView = New-Object System.Windows.Forms.ListView
$ListView.Size = [System.Drawing.Point]"400,400"
$ListView.Location = [System.Drawing.Point]"50,50"
$ListView.CheckBoxes = $true
$ListView.View = [System.Windows.Forms.View]::Details
$ListView.Width = $Form.ClientRectangle.Width - 100
$ListView.Height = $Form.ClientRectangle.Height - 150
$ListView.Anchor = "Top, Left, Right, Bottom"

$ListView.Columns.Add("Title",-2) | Out-Null
$ListView.Columns.Add("GUID",0) | Out-Null
$ListView.Columns.Add("Needs Reboot",-2) | Out-Null
$ListView.add_ItemChecked({if(.$EvalChecks){$OKButton.Enabled = $true}else{$OKButton.Enabled = $false}})
   
Foreach($Update in $NotInstalled.Updates){
        if($Update.InstallationBehavior.CanRequestUserInput -eq $false){
        $ListViewItem = New-Object System.Windows.Forms.ListViewItem($Update.Title)
        #$ListViewItem.SubItems.Add($Update.KBArticleIDs.Item(0)) | Out-Null
        #$ListViewItem.SubItems.Add([int]($Update.MaxDownloadSize / 1MB)) | Out-Null
        #$ListViewItem.SubItems.Add($Update.LastDeploymentChangeTime.ToString()) | Out-Null
        $ListViewItem.SubItems.Add($Update.Identity.UpdateID) | Out-Null
        $ListViewItem.SubItems.Add($Update.RebootRequired.ToString()) | Out-Null
        $ListView.Items.Add($ListViewItem) | Out-Null
    }
}

$EvalChecks = {
    ($ListView.Items | select -ExpandProperty checked | ?{$_ -eq $true} | Measure-Object | select -ExpandProperty count) -gt 0
}

$InstallUpdates = {
    $Form.Hide()
    $CheckedUpdates = $ListView.Items | where{$_.Checked} | select -ExpandProperty SubItems | Where{$_.Text -match "\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b"} | select -ExpandProperty Text
    $Installer = New-Object -ComObject "Microsoft.Update.Installer"
    $UpdateCollection = New-Object -ComObject "Microsoft.Update.UpdateColl"
    $DownloadCollection =  New-Object -ComObject "Microsoft.Update.UpdateColl"

    Foreach($Selected_Update in $CheckedUpdates){
        $UpdateToInstall = $NotInstalled.Updates | Where{$_.Identity.UpdateID -in $Selected_Update}
        #Check if already downloaded, no need to duplicate effort
        if(!$UpdateToInstall.IsDownloaded){
            $DownloadCollection.Add($UpdateToInstall)
        }

        $UpdateCollection.Add($UpdateToInstall)
        if($Installer.RebootRequiredBeforeInstallation){[System.Windows.Forms.MessageBox]::Show("A reboot is required before the remaining updates can be installed.");$Form.Close();break}
    }
       
        $Downloader.Updates = $DownloadCollection
        $Downloader.Download()

        $Installer.Updates = $UpdateCollection
        $Installer.Install()
        $Form.Close()
}

$CancelButton = New-Button -Text "Cancel" -X 425 -Y 485 -ScriptBlock {$ListView.Items | %{$_.Checked = $false};$Form.Close()}
$OKButton = New-Button -Text "Install Selection" -X 175 -Y 485 -ScriptBlock $InstallUpdates

$CheckBox_Select_All = New-Object System.Windows.Forms.CheckBox
$CheckBox_Select_All.Size = [System.Drawing.Point]"100,20"
$CheckBox_Select_All.Location = [System.Drawing.Point]"55,25"
$CheckBox_Select_All.Text = "Select All"
$CheckBox_Select_All.Add_CheckedChanged({
    If($CheckBox_Select_All.Checked){
        $ListView.Items | %{$_.Checked = $true}
    }
    elseif(! $CheckBox_Select_All.Checked){
        $ListView.Items | %{$_.Checked = $false}
    }
})
$Form.Controls.Add($CheckBox_Select_All)

$Title = New-Object System.Windows.Forms.Label
$Title.Text = "The Chicoine Patching Tool"
$Title.Font = "Calibri,20"
$Title.Location = [System.Drawing.Point]"250,10"
$Title.Size = [System.Drawing.Point]"500,40"
   
$Form.Controls.Add($Title)
$Form.Controls.Add($ListView) | Out-Null
$Form.Controls.Add($OKButton)
$Form.Controls.Add($CancelButton)
$Form.Add_Shown({if(.$EvalChecks){$OKButton.Enabled = $true}else{$OKButton.Enabled = $false}})
#[System.Windows.Forms.Application]::Run($Form)
$Form.ShowDialog()

No comments:

Post a Comment