Invoke-WUInstall.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
Function Invoke-WUInstall
{
    <#
 .SYNOPSIS
  Invoke Get-WUInstall remotely.
 
 .DESCRIPTION
  Use Invoke-WUInstall to invoke Windows Update install remotly. It Based on TaskScheduler because
  CreateUpdateDownloader() and CreateUpdateInstaller() methods can't be called from a remote computer - E_ACCESSDENIED.
   
  Note:
  Because we do not have the ability to interact, is recommended use -AcceptAll with WUInstall filters in script block.
  
 .PARAMETER ComputerName
  Specify computer name.
 
 .PARAMETER TaskName
  Specify task name. Default is PSWindowsUpdate.
   
 .PARAMETER Script
  Specify PowerShell script block that you what to run. Default is {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log}
   
 .EXAMPLE
  PS C:\> $Script = {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll -AutoReboot | Out-File C:\PSWindowsUpdate.log}
  PS C:\> Invoke-WUInstall -ComputerName pc1.contoso.com -Script $Script
  ...
  PS C:\> Get-Content \\pc1.contoso.com\c$\PSWindowsUpdate.log
   
 .NOTES
  Author: Michal Gajda
  Blog : http://commandlinegeeks.com/
 
 .LINK
  Get-WUInstall
 #>

    [CmdletBinding(
        SupportsShouldProcess=$True,
        ConfirmImpact="High"
    )]
    param
    (
        [Parameter(ValueFromPipeline=$True,
                    ValueFromPipelineByPropertyName=$True)]
        [String[]]$ComputerName,
        [String]$TaskName = "PSWindowsUpdate",
        [ScriptBlock]$Script = {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log},
        [Switch]$OnlineUpdate
    )

    Begin
    {
        $User = [Security.Principal.WindowsIdentity]::GetCurrent()
        $Role = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)

        if(!$Role)
        {
            Write-Warning "To perform some operations you must run an elevated Windows PowerShell console."    
        } #End If !$Role
        
        $PSWUModule = Get-Module -Name PSWindowsUpdate -ListAvailable
        
        Write-Verbose "Create schedule service object"
        $Scheduler = New-Object -ComObject Schedule.Service
            
        $Task = $Scheduler.NewTask(0)

        $RegistrationInfo = $Task.RegistrationInfo
        $RegistrationInfo.Description = $TaskName
        $RegistrationInfo.Author = $User.Name

        $Settings = $Task.Settings
        $Settings.Enabled = $True
        $Settings.StartWhenAvailable = $True
        $Settings.Hidden = $False

        $Action = $Task.Actions.Create(0)
        $Action.Path = "powershell"
        $Action.Arguments = "-Command $Script"
        
        $Task.Principal.RunLevel = 1    
    }
    
    Process
    {
        ForEach($Computer in $ComputerName)
        {
            If ($pscmdlet.ShouldProcess($Computer,"Invoke WUInstall")) 
            {
                if(Test-Connection -ComputerName $Computer -Quiet)
                {
                    Write-Verbose "Check PSWindowsUpdate module on $Computer"
                    Try
                    {
                        $ModuleTest = Invoke-Command -ComputerName $Computer -ScriptBlock {Get-Module -ListAvailable -Name PSWindowsUpdate} -ErrorAction Stop
                    } #End Try
                    Catch
                    {
                        Write-Warning "Can't access to machine $Computer. Try use: winrm qc"
                        Continue
                    } #End Catch
                    $ModulStatus = $false
                    
                    if($ModuleTest -eq $null -or $ModuleTest.Version -lt $PSWUModule.Version)
                    {
                        if($OnlineUpdate)
                        {
                            Update-WUModule -ComputerName $Computer
                        } #End If $OnlineUpdate
                        else
                        {
                            Update-WUModule -ComputerName $Computer    -LocalPSWUSource (Get-Module -ListAvailable -Name PSWindowsUpdate).ModuleBase
                        } #End Else $OnlineUpdate
                    } #End If $ModuleTest -eq $null -or $ModuleTest.Version -lt $PSWUModule.Version
                    
                    #Sometimes can't connect at first time
                    $Info = "Connect to scheduler and register task on $Computer"
                    for ($i=1; $i -le 3; $i++)
                    {
                        $Info += "."
                        Write-Verbose $Info
                        Try
                        {
                            $Scheduler.Connect($Computer)
                            Break
                        } #End Try
                        Catch
                        {
                            if($i -ge 3)
                            {
                                Write-Error "Can't connect to Schedule service on $Computer" -ErrorAction Stop
                            } #End If $i -ge 3
                            else
                            {
                                sleep -Seconds 1
                            } #End Else $i -ge 3
                        } #End Catch                    
                    } #End For $i=1; $i -le 3; $i++
                    
                    $RootFolder = $Scheduler.GetFolder("\")
                    $SendFlag = 1
                    if($Scheduler.GetRunningTasks(0) | Where-Object {$_.Name -eq $TaskName})
                    {
                        $CurrentTask = $RootFolder.GetTask($TaskName)
                        $Title = "Task $TaskName is curretly running: $($CurrentTask.Definition.Actions | Select-Object -exp Path) $($CurrentTask.Definition.Actions | Select-Object -exp Arguments)"
                        $Message = "What do you want to do?"

                        $ChoiceContiniue = New-Object System.Management.Automation.Host.ChoiceDescription "&Continue Current Task"
                        $ChoiceStart = New-Object System.Management.Automation.Host.ChoiceDescription "Stop and Start &New Task"
                        $ChoiceStop = New-Object System.Management.Automation.Host.ChoiceDescription "&Stop Task"
                        $Options = [System.Management.Automation.Host.ChoiceDescription[]]($ChoiceContiniue, $ChoiceStart, $ChoiceStop)
                        $SendFlag = $host.ui.PromptForChoice($Title, $Message, $Options, 0)
                    
                        if($SendFlag -ge 1)
                        {
                            ($RootFolder.GetTask($TaskName)).Stop(0)
                        } #End If $SendFlag -eq 1    
                        
                    } #End If !($Scheduler.GetRunningTasks(0) | Where-Object {$_.Name -eq $TaskName})
                        
                    if($SendFlag -eq 1)
                    {
                        $RootFolder.RegisterTaskDefinition($TaskName, $Task, 6, "SYSTEM", $Null, 1) | Out-Null
                        $RootFolder.GetTask($TaskName).Run(0) | Out-Null
                    } #End If $SendFlag -eq 1
                    
                    #$RootFolder.DeleteTask($TaskName,0)
                } #End If Test-Connection -ComputerName $Computer -Quiet
                else
                {
                    Write-Warning "Machine $Computer is not responding."
                } #End Else Test-Connection -ComputerName $Computer -Quiet
            } #End If $pscmdlet.ShouldProcess($Computer,"Invoke WUInstall")
        } #End ForEach $Computer in $ComputerName
        Write-Verbose "Invoke-WUInstall complete."
    }
    
    End {}

}