-
Notifications
You must be signed in to change notification settings - Fork 141
/
Winget-Upgrade.ps1
390 lines (348 loc) · 16.7 KB
/
Winget-Upgrade.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
<# LOAD FUNCTIONS #>
#Get the Working Dir
$Script:WorkingDir = $PSScriptRoot
#Get Functions
Get-ChildItem "$WorkingDir\functions" | ForEach-Object { . $_.FullName }
<# MAIN #>
#Config console output encoding
$null = cmd /c ''
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
#Log initialisation
$LogFile = "$WorkingDir\logs\updates.log"
#Check if running account is system or interactive logon
$Script:IsSystem = [System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem
#Check for current session ID (O = system without ServiceUI)
$Script:SessionID = [System.Diagnostics.Process]::GetCurrentProcess().SessionId
#Check if running as system
if ($IsSystem) {
#Check if running with session ID 0
if ($SessionID -eq 0) {
#Check if ServiceUI exists
$ServiceUI = Test-Path "$WorkingDir\ServiceUI.exe"
if ($ServiceUI) {
#Check if any connected user
$explorerprocesses = @(Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
if ($explorerprocesses.Count -gt 0) {
#Rerun WAU in system context with ServiceUI
& $WorkingDir\ServiceUI.exe -process:explorer.exe $env:windir\System32\wscript.exe \`"$WorkingDir\Invisible.vbs\`" \`"powershell.exe -NoProfile -ExecutionPolicy Bypass -File \`"\`"$WorkingDir\winget-upgrade.ps1\`"\`"\`"
Exit 0
}
else {
Write-ToLog -LogMsg "CHECK FOR APP UPDATES (System context)" -IsHeader
}
}
else {
Write-ToLog -LogMsg "CHECK FOR APP UPDATES (System context - No ServiceUI)" -IsHeader
}
}
else {
Write-ToLog -LogMsg "CHECK FOR APP UPDATES (System context - Connected user)" -IsHeader
}
}
else {
Write-ToLog -LogMsg "CHECK FOR APP UPDATES (User context)" -IsHeader
}
#Get settings and Domain/Local Policies (GPO) if activated.
$Script:WAUConfig = Get-WAUConfig
if ($($WAUConfig.WAU_ActivateGPOManagement -eq 1)) {
Write-ToLog "WAU Policies management Enabled."
}
#Log running context and more...
if ($IsSystem) {
# Maximum number of log files to keep. Default is 3. Setting MaxLogFiles to 0 will keep all log files.
$MaxLogFiles = $WAUConfig.WAU_MaxLogFiles
if ($null -eq $MaxLogFiles) {
[int32] $MaxLogFiles = 3
}
else {
[int32] $MaxLogFiles = $MaxLogFiles
}
# Maximum size of log file.
$MaxLogSize = $WAUConfig.WAU_MaxLogSize
if (!$MaxLogSize) {
[int64] $MaxLogSize = 1048576 # in bytes, default is 1048576 = 1 MB
}
else {
[int64] $MaxLogSize = $MaxLogSize
}
#LogRotation if System
$LogRotate = Invoke-LogRotation $LogFile $MaxLogFiles $MaxLogSize
if ($LogRotate -eq $False) {
Write-ToLog "An Exception occured during Log Rotation..."
}
#Run post update actions if necessary if run as System
if (!($WAUConfig.WAU_PostUpdateActions -eq 0)) {
Invoke-PostUpdateActions
}
#Run Scope Machine funtion if run as System
Add-ScopeMachine
}
#Get Notif Locale function
$LocaleDisplayName = Get-NotifLocale
Write-ToLog "Notification Level: $($WAUConfig.WAU_NotificationLevel). Notification Language: $LocaleDisplayName" "Cyan"
#Check network connectivity
if (Test-Network) {
#Check if Winget is installed and get Winget cmd
$Script:Winget = Get-WingetCmd
if ($Winget) {
#Log Winget installed version
$WingetVer = & $Winget --version
Write-ToLog "Winget Version: $WingetVer"
#Get Current Version
$WAUCurrentVersion = $WAUConfig.DisplayVersion
Write-ToLog "WAU current version: $WAUCurrentVersion"
if ($IsSystem) {
#Check if WAU update feature is enabled or not if run as System
$WAUDisableAutoUpdate = $WAUConfig.WAU_DisableAutoUpdate
#If yes then check WAU update if run as System
if ($WAUDisableAutoUpdate -eq 1) {
Write-ToLog "WAU AutoUpdate is Disabled." "Gray"
}
else {
Write-ToLog "WAU AutoUpdate is Enabled." "Green"
#Get Available Version
$Script:WAUAvailableVersion = Get-WAUAvailableVersion
#Compare
if ([version]$WAUAvailableVersion.Replace("-", ".") -ne [version]$WAUCurrentVersion.Replace("-", ".")) {
#If new version is available, update it
Write-ToLog "WAU Available version: $WAUAvailableVersion" "Yellow"
Update-WAU
}
else {
Write-ToLog "WAU is up to date." "Green"
}
}
#Delete previous list_/winget_error (if they exist) if run as System
if (Test-Path "$WorkingDir\logs\error.txt") {
Remove-Item "$WorkingDir\logs\error.txt" -Force
}
#Get External ListPath if run as System
if ($WAUConfig.WAU_ListPath) {
$ListPathClean = $($WAUConfig.WAU_ListPath.TrimEnd(" ", "\", "/"))
Write-ToLog "WAU uses External Lists from: $ListPathClean"
if ($ListPathClean -ne "GPO") {
$NewList = Test-ListPath $ListPathClean $WAUConfig.WAU_UseWhiteList $WAUConfig.InstallLocation.TrimEnd(" ", "\")
if ($ReachNoPath) {
Write-ToLog "Couldn't reach/find/compare/copy from $ListPathClean..." "Red"
if ($ListPathClean -notlike "http*") {
if (Test-Path -Path "$ListPathClean" -PathType Leaf) {
Write-ToLog "PATH must end with a Directory, not a File..." "Red"
}
}
else {
if ($ListPathClean -match "_apps.txt") {
Write-ToLog "PATH must end with a Directory, not a File..." "Red"
}
}
$Script:ReachNoPath = $False
}
if ($NewList) {
Write-ToLog "Newer List downloaded/copied to local path: $($WAUConfig.InstallLocation.TrimEnd(" ", "\"))" "Yellow"
}
else {
if ($WAUConfig.WAU_UseWhiteList -and (Test-Path "$WorkingDir\included_apps.txt")) {
Write-ToLog "List (white) is up to date." "Green"
}
elseif (!$WAUConfig.WAU_UseWhiteList -and (Test-Path "$WorkingDir\excluded_apps.txt")) {
Write-ToLog "List (black) is up to date." "Green"
}
else {
Write-ToLog "Critical: White/Black List doesn't exist, exiting..." "Red"
New-Item "$WorkingDir\logs\error.txt" -Value "White/Black List doesn't exist" -Force
Exit 1
}
}
}
}
#Get External ModsPath if run as System
if ($WAUConfig.WAU_ModsPath) {
$ModsPathClean = $($WAUConfig.WAU_ModsPath.TrimEnd(" ", "\", "/"))
Write-ToLog "WAU uses External Mods from: $ModsPathClean"
if ($WAUConfig.WAU_AzureBlobSASURL) {
$NewMods, $DeletedMods = Test-ModsPath $ModsPathClean $WAUConfig.InstallLocation.TrimEnd(" ", "\") $WAUConfig.WAU_AzureBlobSASURL.TrimEnd(" ")
}
else {
$NewMods, $DeletedMods = Test-ModsPath $ModsPathClean $WAUConfig.InstallLocation.TrimEnd(" ", "\")
}
if ($ReachNoPath) {
Write-ToLog "Couldn't reach/find/compare/copy from $ModsPathClean..." "Red"
$Script:ReachNoPath = $False
}
if ($NewMods -gt 0) {
Write-ToLog "$NewMods newer Mods downloaded/copied to local path: $($WAUConfig.InstallLocation.TrimEnd(" ", "\"))\mods" "Yellow"
}
else {
if (Test-Path "$WorkingDir\mods\*.ps1") {
Write-ToLog "Mods are up to date." "Green"
}
else {
Write-ToLog "No Mods are implemented..." "Yellow"
}
}
if ($DeletedMods -gt 0) {
Write-ToLog "$DeletedMods Mods deleted (not externally managed) from local path: $($WAUConfig.InstallLocation.TrimEnd(" ", "\"))\mods" "Red"
}
}
#Test if _WAU-mods.ps1 exist: Mods for WAU (if Network is active/any Winget is installed/running as SYSTEM)
$Mods = "$WorkingDir\mods"
if (Test-Path "$Mods\_WAU-mods.ps1") {
Write-ToLog "Running Mods for WAU..." "Yellow"
& "$Mods\_WAU-mods.ps1"
$ModsExitCode = $LASTEXITCODE
#If _WAU-mods.ps1 has ExitCode 1 - Re-run WAU
if ($ModsExitCode -eq 1) {
Write-ToLog "Re-run WAU"
Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"$WorkingDir\winget-upgrade.ps1`""
Exit
}
}
}
if ($($WAUConfig.WAU_ListPath) -eq "GPO") {
$Script:GPOList = $True
}
#Get White or Black list
if ($WAUConfig.WAU_UseWhiteList -eq 1) {
Write-ToLog "WAU uses White List config"
$toUpdate = Get-IncludedApps
$UseWhiteList = $true
}
else {
Write-ToLog "WAU uses Black List config"
$toSkip = Get-ExcludedApps
}
#Fix and count the array if GPO List as ERROR handling!
if ($GPOList) {
if ($UseWhiteList) {
$WhiteList = $toUpdate.GetUpperBound(0)
if ($null -eq $WhiteList) {
Write-ToLog "Critical: Whitelist doesn't exist in GPO, exiting..." "Red"
New-Item "$WorkingDir\logs\error.txt" -Value "Whitelist doesn't exist in GPO" -Force
Exit 1
}
$toUpdate = $toUpdate.Data
}
else {
$BlackList = $toSkip.GetUpperBound(0)
if ($null -eq $BlackList) {
Write-ToLog "Critical: Blacklist doesn't exist in GPO, exiting..." "Red"
New-Item "$WorkingDir\logs\error.txt" -Value "Blacklist doesn't exist in GPO" -Force
Exit 1
}
$toSkip = $toSkip.Data
}
}
#Get outdated Winget packages
Write-ToLog "Checking application updates on Winget Repository..." "yellow"
$outdated = Get-WingetOutdatedApps
#If something unusual happened
if ($outdated -like "An unusual*") {
Write-ToLog "$outdated" "cyan"
$outdated = $False
}
#Run only if $outdated is populated!
if ($outdated) {
#Log list of app to update
foreach ($app in $outdated) {
#List available updates
$Log = "-> Available update : $($app.Name). Current version : $($app.Version). Available version : $($app.AvailableVersion)."
$Log | Write-host
$Log | out-file -filepath $LogFile -Append
}
#Count good update installations
$Script:InstallOK = 0
#Trick under user context when -BypassListForUsers is used
if ($IsSystem -eq $false -and $WAUConfig.WAU_BypassListForUsers -eq 1) {
Write-ToLog "Bypass system list in user context is Enabled."
$UseWhiteList = $false
$toSkip = $null
}
#If White List
if ($UseWhiteList) {
#For each app, notify and update
foreach ($app in $outdated) {
if (($toUpdate -contains $app.Id) -and $($app.Version) -ne "Unknown") {
Update-App $app
}
#if current app version is unknown
elseif ($($app.Version) -eq "Unknown") {
Write-ToLog "$($app.Name) : Skipped upgrade because current version is 'Unknown'" "Gray"
}
#if app is in "excluded list"
else {
Write-ToLog "$($app.Name) : Skipped upgrade because it is not in the included app list" "Gray"
}
}
}
#If Black List or default
else {
#For each app, notify and update
foreach ($app in $outdated) {
if (-not ($toSkip -contains $app.Id) -and $($app.Version) -ne "Unknown") {
Update-App $app
}
#if current app version is unknown
elseif ($($app.Version) -eq "Unknown") {
Write-ToLog "$($app.Name) : Skipped upgrade because current version is 'Unknown'" "Gray"
}
#if app is in "excluded list"
else {
Write-ToLog "$($app.Name) : Skipped upgrade because it is in the excluded app list" "Gray"
}
}
}
if ($InstallOK -gt 0) {
Write-ToLog "$InstallOK apps updated ! No more update." "Green"
}
}
if ($InstallOK -eq 0 -or !$InstallOK) {
Write-ToLog "No new update." "Green"
}
#Check if user context is activated during system run
if ($IsSystem) {
#Adds SymLink if Intune managed
$IntuneLogPath = "${env:ProgramData}\Microsoft\IntuneManagementExtension\Logs"
if ((Test-Path "$IntuneLogPath") -and !(Test-Path "$IntuneLogPath\WAU-updates.log")) {
Write-ToLog "Creating SymLink for log file (WAU-updates) in Intune Management Extension log folder" "Yellow"
New-Item -Path "$IntuneLogPath\WAU-updates.log" -ItemType SymbolicLink -Value $LogFile -Force -ErrorAction SilentlyContinue | Out-Null
}
#Run WAU in user context if feature is activated
if ($WAUConfig.WAU_UserContext -eq 1) {
#Create User context task if not existing
$UserContextTask = Get-ScheduledTask -TaskName 'Winget-AutoUpdate-UserContext' -ErrorAction SilentlyContinue
if (!$UserContextTask) {
#Create the scheduled task in User context
$taskAction = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "`"$($WAUConfig.InstallLocation)\Invisible.vbs`" `"powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"`"`"$($WAUConfig.InstallLocation)\winget-upgrade.ps1`"`""
$taskUserPrincipal = New-ScheduledTaskPrincipal -GroupId S-1-5-11
$taskSettings = New-ScheduledTaskSettingsSet -Compatibility Win8 -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit 03:00:00
$task = New-ScheduledTask -Action $taskAction -Principal $taskUserPrincipal -Settings $taskSettings
Register-ScheduledTask -TaskName 'Winget-AutoUpdate-UserContext' -TaskPath 'WAU' -InputObject $task -Force | Out-Null
Write-ToLog "-> User Context task created."
#Load it
$UserContextTask = Get-ScheduledTask -TaskName 'Winget-AutoUpdate-UserContext' -ErrorAction SilentlyContinue
}
$explorerprocesses = @(Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0) {
Write-ToLog "No explorer process found / Nobody interactively logged on..."
}
Else {
#Get Winget system apps to excape them befor running user context
Write-ToLog "User logged on, get a list of installed Winget apps in System context..."
Get-WingetSystemApps
#Run user context scheduled task
Write-ToLog "Starting WAU in User context..."
$null = $UserContextTask | Start-ScheduledTask -ErrorAction SilentlyContinue
Exit 0
}
}
}
}
else {
Write-ToLog "Critical: Winget not installed or detected, exiting..." "red"
New-Item "$WorkingDir\logs\error.txt" -Value "Winget not installed or detected" -Force
Write-ToLog "End of process!" "Cyan"
Exit 1
}
}
#End
Write-ToLog "End of process!" "Cyan"
Start-Sleep 3