├── .attachments
├── Dashboard-B.png
└── UpdateReporting001-level.PNG
├── Import-SSRSReports.ps1
├── README.md
└── SourceFiles
├── Compare Update Compliance.rdl
├── Software Updates Compliance - ErrorList.rdl
├── Software Updates Compliance - Overview ABC Last ADDS Logon.rdl
├── Software Updates Compliance - Overview ABC Last Policy Update.rdl
├── Software Updates Compliance - Overview ABC Last Reboot.rdl
├── Software Updates Compliance - Overview ABC Last Update Install.rdl
├── Software Updates Compliance - Overview Pending Reboot.rdl
├── Software Updates Compliance - Overview Updates missing not deployed.rdl
├── Software Updates Compliance - Overview compliance list.rdl
├── Software Updates Compliance - Overview current rollup status.rdl
├── Software Updates Compliance - Overview last rollup status.rdl
├── Software Updates Compliance - Overview.rdl
├── Software Updates Compliance - Per device deployments.rdl
├── Software Updates Compliance - Per device.rdl
├── UpdatesInstallErrorList.rsd
├── UpdatesStatePerDevice.rsd
├── UpdatesSummary.rsd
└── UpdatesSummary.sql
/.attachments/Dashboard-B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonasatgit/updatereporting/0ca927dc4442915997421d70dc645b8172f6f7bf/.attachments/Dashboard-B.png
--------------------------------------------------------------------------------
/.attachments/UpdateReporting001-level.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonasatgit/updatereporting/0ca927dc4442915997421d70dc645b8172f6f7bf/.attachments/UpdateReporting001-level.PNG
--------------------------------------------------------------------------------
/Import-SSRSReports.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Uploads SQL Server Reporting Services report and dataset files.
4 |
5 | .DESCRIPTION
6 | The script will change the content of rdl and rsd files and will upload them to a SQL Server Reporting Services (SSRS) of your choice.
7 | The rdl and rsd files contain specific strings which are simply replaced by the parameter values of this script.
8 |
9 | Disclaimer
10 | This sample script is not supported under any Microsoft standard support program or service. This sample
11 | script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties
12 | including, without limitation, any implied warranties of merchantability or of fitness for a particular
13 | purpose. The entire risk arising out of the use or performance of this sample script and documentation
14 | remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation,
15 | production, or delivery of this script be liable for any damages whatsoever (including, without limitation,
16 | damages for loss of business profits, business interruption, loss of business information, or other
17 | pecuniary loss) arising out of the use of or inability to use this sample script or documentation, even
18 | if Microsoft has been advised of the possibility of such damages.
19 |
20 | .PARAMETER ReportServerURI
21 | The URL of the SQL Reporting Services Server. For example: http://reportserver.domain.local/reportserver
22 |
23 | .PARAMETER TargetFolderPath
24 | The folder were the reports should be placed in. I created a folder called "Custom_UpdateReporting" below the default MECM reporting folder. My sitecode is P11, so the default folder is called "ConfigMgr_P11".
25 | For example: "ConfigMgr_P11/Custom_UpdateReporting"
26 | Use "/"" instead of "\"" because it's a website
27 |
28 | .PARAMETER TargetDataSourcePath
29 | The path should point to the default ConfigMgr/MECM data source.
30 | In my case the Sitecode is P11 and the default data source is therefore in the folder "ConfigMgr_P11" and has the ID "{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}"
31 | The path with the default folder is required. For example: "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}""
32 | Use "/"" instead of "\"" because it's a website
33 |
34 | .PARAMETER DefaultCollection
35 | The report can show data of a default collection when it will be run, so that you don't need to provide a collection name each time you run the report.
36 | The default value is "SMS00001" which is the CollectionID of "All Systems", which might not be the best choice for bigger environments.
37 |
38 | .PARAMETER DefaultCollectionFilter
39 | The filter is used to find the collection you are interested in and the value needs to match the name of the collection you choose to be the default collection for the parameter "defaultCollection".
40 | In my case "All%" or All Syst% or "Servers%" to get the "Servers of the environment" collection for example.
41 |
42 | .PARAMETER DoNotHideReports
43 | Array of reports which should not be set to hidden. You should not use the parameter unless you really want more reports to be visible.
44 |
45 | .PARAMETER $DoNotUpload
46 | If used each reports will be changed to have the correct values, but will not be uploaded.
47 | That might be helpful, if you do not have the rights to upload and need to give the files to another person so that they can be uploaded manually
48 |
49 | .PARAMETER ReportSourcePath
50 | The script will use the script root path to look for a folder called "Sourcefiles" and will copy all the report files from there.
51 | But you could also provide a different path where the script should look for a "Sourcefiles" folder.
52 |
53 | .PARAMETER ForceLegacyFormat
54 | The report xml definition will be changed to the older pre SSRS 2016 format. That way the reports also work with SSRS 2014 for example.
55 |
56 | .PARAMETER ForceLegacyCardinalitySQL2016SP1AndHigher
57 | Will add SQL query hint "OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))" to important queries to increase performance.
58 | Works only with SQL Server 2016 SP1 and higher (SQL version >= 13.0.4001.0)
59 | More infos can be found here: https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
60 |
61 | .PARAMETER ForceLegacyCardinalityOlderThanSQL2016SP1
62 | Will add SQL query trace flag "OPTION (QUERYTRACEON 9481)" to important queries to increase performance.
63 | Works only with older SQL version than SQL Server 2016 SP1 (SQL version < 13.0.4001.0)
64 | More infos can be found here: https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
65 |
66 | .PARAMETER TryOverwrite
67 | If set, the script will try to overwrite existing reports. In some cases not all settings are overwritten unfortunately, hence the name TRY-Overwrite
68 |
69 | .INPUTS
70 | None. You cannot pipe objects to Import-SSRSReports.ps1
71 |
72 | .OUTPUTS
73 | Just normal console output. Nothing to work with.
74 |
75 | .EXAMPLE
76 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}"
77 |
78 | .EXAMPLE
79 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}" -TryOverwrite
80 |
81 | .EXAMPLE
82 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}" -ForceLegacyFormat -ForceLegacyCardinalitySQL2016SP1AndHigher
83 |
84 | .EXAMPLE
85 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}" -DoNotUpload
86 |
87 | .EXAMPLE
88 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}" -ForceLegacyCardinalitySQL2016SP1AndHigher
89 |
90 | .EXAMPLE
91 | PS> .\Import-SSRSReports.ps1 -ReportServerURI "http://reportserver.domain.local/reportserver" -TargetFolderPath "ConfigMgr_P11/Custom_UpdateReporting" -TargetDataSourcePath "ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}" -DefaultCollectionID "P1100012" -DefaultCollectionFilter "All Servers of Contoso%"
92 |
93 | .LINK
94 | https://github.com/jonasatgit/updatereporting
95 |
96 | .LINK
97 | https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/mastering-configuration-manager-patch-compliance-reporting/ba-p/1415088
98 |
99 | #>
100 | [CmdletBinding(DefaultParametersetName='None')]
101 | param(
102 |
103 | [parameter(Mandatory=$true)]
104 | [string]$ReportServerUri = "http://reportserver.domain.local/reportserver",
105 |
106 | [parameter(Mandatory=$true)]
107 | [string]$TargetFolderPath = 'ConfigMgr_P11/Custom_UpdateReporting',
108 |
109 | [parameter(Mandatory=$true)]
110 | [string]$TargetDataSourcePath = 'ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}',
111 |
112 | [parameter(ParameterSetName = 'CollectionInfo',Mandatory=$false)]
113 | [string]$DefaultCollectionID = 'SMS00001',
114 |
115 | [parameter(ParameterSetName = 'CollectionInfo',Mandatory=$true)]
116 | [string]$DefaultCollectionFilter = 'All%',
117 |
118 | [parameter(Mandatory=$false)]
119 | [array]$DoNotHideReports = @('Software Updates Compliance - Overview','Software Updates Compliance - Per device','Software Updates Compliance - Per device deployments','Software Updates Compliance - Overview compliance list','Compare Update Compliance','Software Updates Compliance - Offline Scan Results'),
120 |
121 | [parameter(Mandatory=$false)]
122 | [Switch]$DoNotUpload,
123 |
124 | #[parameter(Mandatory=$false)]
125 | #[bool]$UseViewForDataset = $false,
126 |
127 | [parameter(Mandatory=$false)]
128 | [switch]$ForceLegacyFormat,
129 |
130 | [parameter(Mandatory=$false)]
131 | [switch]$ForceLegacyCardinalitySQL2016SP1AndHigher,
132 |
133 | [parameter(Mandatory=$false)]
134 | [switch]$ForceLegacyCardinalityOlderThanSQL2016SP1,
135 |
136 | [parameter(Mandatory=$false)]
137 | [string]$ReportSourcePath = $($PSScriptRoot),
138 |
139 | [parameter(Mandatory=$false)]
140 | [switch]$TryOverwrite
141 |
142 | )
143 |
144 | #[string]$datasetUsingSQLView = 'UpdatesSummaryView'
145 |
146 | $cleanFolder = "$reportSourcePath\SourceFiles"
147 | $workFolder = "$reportSourcePath\work"
148 |
149 | $overwriteReportItem = $false
150 | if ($TryOverwrite)
151 | {
152 | $overwriteReportItem = $true
153 | Write-Host "Will try to overwrite existing reports..." -ForegroundColor Yellow
154 | }
155 |
156 | if ($ForceLegacyCardinalityOlderThanSQL2016SP1 -and $ForceLegacyCardinalitySQL2016SP1AndHigher)
157 | {
158 | Write-Host "Get-Help .\Import-SSRSReports.ps1 -Examples"
159 | Get-Help .\Import-SSRSReports.ps1 -Examples
160 | Write-Host " "
161 | Write-host "Use either ForceLegacyCardinalityOlderThanSQL2016SP1 or ForceLegacyCardinalitySQL2016SP1AndHigher" -ForegroundColor Yellow
162 | Write-Host "Run `"Get-Help .\Import-SSRSReports.ps1 -Full`" to get help" -ForegroundColor Yellow
163 | break
164 | }
165 |
166 | # not using validatepattern to genereate nice error messages
167 | if ($ReportServerUri -notmatch '^[a-z0-9\./:\{\}\-_ ]+$')
168 | {
169 | Write-Host "Get-Help .\Import-SSRSReports.ps1 -Examples"
170 | Get-Help .\Import-SSRSReports.ps1 -Examples
171 | Write-Host " "
172 | Write-host "Parameter `"ReportServerUri`" needs to match regex: '^[a-z0-9\./:\{\}\-_ ]+$'" -ForegroundColor Yellow
173 | Write-host "Please use slash `"/`" instead of backslash `"\`" for parameter `"ReportServerUri`"" -ForegroundColor Yellow
174 | Write-Host "Run `"Get-Help .\Import-SSRSReports.ps1 -Full`" to get help" -ForegroundColor Yellow
175 | break
176 | }
177 |
178 |
179 | if ($TargetFolderPath -notmatch '^[a-z0-9\./:\{\}\-_ ]+$')
180 | {
181 | Write-Host "Get-Help .\Import-SSRSReports.ps1 -Examples"
182 | Get-Help .\Import-SSRSReports.ps1 -Examples
183 | Write-Host " "
184 | Write-host "Parameter `"TargetFolderPath`" needs to match regex: '^[a-z0-9\./:\{\}\-_ ]+$'" -ForegroundColor Yellow
185 | Write-host "Please use slash `"/`" instead of backslash `"\`" for parameter `"TargetFolderPath`"" -ForegroundColor Yellow
186 | Write-Host "Run `"Get-Help .\Import-SSRSReports.ps1 -Full`" to get help" -ForegroundColor Yellow
187 | break
188 | }
189 |
190 |
191 | if ($TargetDataSourcePath -notmatch '^[a-z0-9\./:\{\}\-_ ]+$')
192 | {
193 | Write-Host "Get-Help .\Import-SSRSReports.ps1 -Examples"
194 | Get-Help .\Import-SSRSReports.ps1 -Examples
195 | Write-Host " "
196 | Write-host "Parameter `"TargetDataSourcePath`" needs to match regex: '^[a-z0-9\./:\{\}\-_ ]+$'" -ForegroundColor Yellow
197 | Write-host "Please use slash `"/`" instead of backslash `"\`" for parameter `"TargetDataSourcePath`"" -ForegroundColor Yellow
198 | Write-Host "Run `"Get-Help .\Import-SSRSReports.ps1 -Full`" to get help" -ForegroundColor Yellow
199 | break
200 | }
201 |
202 |
203 | if ($DefaultCollectionFilter -match '&')
204 | {
205 | Write-Host "Get-Help .\Import-SSRSReports.ps1 -Examples"
206 | Get-Help .\Import-SSRSReports.ps1 -Examples
207 | Write-Host " "
208 | Write-host "Parameter `"DefaultCollectionFilter`" should not contain the ampersand sign `"&`"" -ForegroundColor Yellow
209 | Write-Host "Run `"Get-Help .\Import-SSRSReports.ps1 -Full`" to get help" -ForegroundColor Yellow
210 | break
211 | }
212 |
213 |
214 | if (-not (Test-Path $cleanFolder))
215 | {
216 | Write-Host "Folder `"$($cleanFolder)`" not found!" -ForegroundColor Yellow
217 | break
218 | }
219 |
220 | if (-not (Test-Path $workFolder))
221 | {
222 | $null = New-Item -ItemType "directory" -Path $workFolder -Force
223 | }
224 | else
225 | {
226 | Get-ChildItem $workFolder | Remove-Item -Force
227 | }
228 | Write-host "Copy `"$($cleanFolder)\*`" to `"$($workFolder)\`"" -ForegroundColor Green
229 | $null = Copy-Item -Path "$($cleanFolder)\*" -Destination "$($workFolder)\" -Force
230 |
231 | [array]$reportsToWorkWith = Get-ChildItem -Path "$workFolder" | Where-Object {$_.Extension -eq '.rdl' -or $_.Extension -eq '.rsd'}
232 | Write-host "Found $($reportsToWorkWith.Count) .rdl and .rsd files in `"$reportSourcePath\work`"" -ForegroundColor Green
233 | if ($reportsToWorkWith.Count -gt 0)
234 | {
235 | if ($ForceLegacyFormat)
236 | {
237 | Write-Host "Changing xml format to work with pre 2016 SSRS versions" -ForegroundColor Yellow
238 | }
239 |
240 | if ($ForceLegacyCardinalitySQL2016SP1AndHigher)
241 | {
242 | Write-Host "Will add SQL query hint: `"OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))`" to larger queries" -ForegroundColor Yellow
243 | }
244 |
245 | if ($ForceLegacyCardinalityOlderThanSQL2016SP1)
246 | {
247 | Write-Host "Will add SQL query trace flag `"OPTION (QUERYTRACEON 9481)`" to larger queries" -ForegroundColor Yellow
248 | }
249 |
250 | $reportsToWorkWith | ForEach-Object {
251 |
252 | Write-host "Working on: $($_.Name)" -ForegroundColor Green
253 |
254 | $reportContent = ''
255 | $reportContent = Get-Content -Path $_.FullName
256 | # simply replacing the neccesary parts
257 | $reportContent = $reportContent.Replace("/ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}","/$($targetDataSourcePath)")
258 | $reportContent = $reportContent.Replace("/ConfigMgr_P11/Custom_UpdateReporting","/$($targetFolderPath)")
259 | $reportContent = $reportContent.Replace("http://reportserver.domain.local/reportserver","$($ReportServerUri)") # case sensitive
260 | $reportContent = $reportContent.Replace("http://reportserver.domain.local/ReportServer","$($ReportServerUri)") # case sensitive
261 | $reportContent = $reportContent.Replace("/ConfigMgr_P11/Custom_UpdateReporting/","/$($targetFolderPath)/")
262 | $reportContent = $reportContent.Replace("All%","$defaultCollectionFilter")
263 | $reportContent = $reportContent.Replace('SMS00001',"$($defaultCollectionID)")
264 |
265 | if ($ForceLegacyCardinalitySQL2016SP1AndHigher) # uncomment SQL hint
266 | {
267 | $reportContent = $reportContent.Replace('--OPTION (USE HINT','OPTION (USE HINT')
268 | }
269 |
270 | if ($ForceLegacyCardinalityOlderThanSQL2016SP1)
271 | {
272 | $reportContent = $reportContent.Replace('--OPTION (QUERYTRACEON 9481)','OPTION (QUERYTRACEON 9481)')
273 | }
274 |
275 |
276 | <#
277 | if ($UseViewForDataset)
278 | {
279 | $reportContent = $reportContent.Replace('UpdatesSummary',"$($datasetUsingSQLView)")
280 | }
281 | #>
282 |
283 | if ($ForceLegacyFormat)
284 | {
285 | # Changing xml format to work with pre 2016 SSRS versions
286 | [xml]$ContentXML=$reportContent
287 | if ($ContentXML.report.xmlns -eq "http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition")
288 | {
289 | $ContentXML.report.xmlns = "http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition"
290 | $ContentXML.report.SetAttribute("cl","http://schemas.microsoft.com/sqlserver/reporting/2010/01/componentdefinition")
291 | }
292 | if ($ContentXML.report.ReportParameterslayout -ne $NULL)
293 | {
294 | [void]$ContentXML.Report.RemoveChild($ContentXML.report.ReportParameterslayout)
295 | }
296 | $ContentXML.Save($_.FullName)
297 | }
298 | else
299 | {
300 | # save all the changes to the file
301 | $reportContent | Out-File -FilePath $($_.FullName) -Encoding utf8 -Force
302 | }
303 | }
304 |
305 | if (-NOT ($DoNotUpload))
306 | {
307 | Write-host "Connecting to: $ReportServerUri..." -ForegroundColor Green
308 |
309 | $ReportServerUriFull = "$ReportServerUri/ReportService2010.asmx?wsdl"
310 | $ReportServerConnection = New-WebServiceProxy -Uri $ReportServerUriFull -Namespace "SSRS" -UseDefaultCredential;
311 | if($ReportServerConnection)
312 | {
313 | Write-host "Connected to: $ReportServerUri" -ForegroundColor Green
314 |
315 | # import datasets first to make them available to reports
316 | $reportsToWorkWith | Sort-Object Extension -Descending | ForEach-Object {
317 | Write-host "Uploading: $($_.Name)..." -ForegroundColor Green
318 |
319 | $reportName = [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
320 | $reportBytes = [System.IO.File]::ReadAllBytes($_.FullName)
321 |
322 | $targetPath = "/$targetFolderPath"
323 |
324 | if($_.Extension -eq '.rsd')
325 | {
326 | $itemType = "DataSet"
327 | }
328 | else
329 | {
330 | $itemType = "Report"
331 | }
332 | $warnings = $null
333 | $null = $ReportServerConnection.CreateCatalogItem(
334 | $itemType, # Catalog item type: Report, Model, Dataset, Component, Resource, and DataSource
335 | $reportName, # Name of the item
336 | $targetPath, # Destination folder
337 | $overwriteReportItem, # Overwrite report if it exists, not all settings are overwritten, therefore set to false. Delete items manually or use TryOverwrite parameter
338 | $reportBytes, # Bytes of item
339 | $null, # Item properties
340 | [ref]$warnings) # Warnings during upload
341 |
342 | if(-NOT($warnings -eq $null))
343 | {
344 | $warnings | ForEach-Object {
345 | Write-Host "Warning: $($_.Message)" -ForegroundColor Yellow
346 | }
347 | }
348 |
349 | # hide all reports exept for reports found in $doNotHideReports
350 | if($doNotHideReports -notcontains $reportName)
351 | {
352 | $Properties = $ReportServerConnection.GetProperties("$targetPath/$reportName",$tmp)
353 | $prop = $Properties | Where-Object {$_.Name -eq 'Hidden'}
354 | $prop.Value = $true
355 | $ReportServerConnection.SetProperties("$targetPath/$reportName",$prop)
356 | }
357 | }
358 |
359 | }
360 | else
361 | {
362 | Write-host "Problem with connection..." -ForegroundColor Yellow
363 | }
364 | }
365 | else
366 | {
367 | Write-host "Parameter is set to NOT upload any reports to: $ReportServerUri" -ForegroundColor Yellow
368 | }
369 | }
370 | Write-host "End of script!" -ForegroundColor Green
371 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Original blog and documentation:
2 | The original blog and documentation can be found [HERE](https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/mastering-configuration-manager-patch-compliance-reporting/ba-p/1415088 "Mastering configuration manager patch compliance reporting")
3 |
4 |
5 | # Other blogs
6 | All my other blogs can be found [HERE](https://aka.ms/JonasOhmsenBlogs "JonasOhmsenBlogs")
7 | and [HERE](https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/mastering-configuration-manager-bandwidth-limitations-for-vpn/ba-p/1280002 "Mastering Configuration Manager Bandwidth limitations for VPN connected Clients")
8 |
9 |
10 | # MEM/MECM/ConfigMgr patch compliance report solution
11 | 
12 | 
13 |
14 |
15 | # Changes
16 | (The version number can be found in the lower left corner of the dashboard. No version number means v1.0)
17 |
18 | ## 2024-05-06 v4.1:
19 | 1. Changed update compliance definition and removed the dependency on "LastInstallTime"
20 |
21 | ## 2023-11-10 v4.0:
22 | 1. Added more possible error fix actions
23 |
24 | ## 2022-10-12 v3.9:
25 | 1. Added logic to hide zero values in diagrams based on feedback from original blog: [HERE](https://aka.ms/JonasOhmsenBlogs "JonasOhmsenBlogs")
26 | 1. Fixed minor issues
27 |
28 | ## 2022-05-06 v3.8:
29 | 1. Fixed a sub-report link problem for WSUS error and install error list
30 |
31 | ## 2022-04-04 v3.7:
32 | 1. Added parameter to show data based on current or previous month. The previous month setting is only applicable if updates are deployed with a month delay and does not rely on historical data
33 | 1. Changed the way compliance for update rollups are shown between first day of month and second Tuesday based on: https://github.com/jonasatgit/updatereporting/pull/11
34 | 1. Changed the "exclude future deployments" parameter to be able to filter out deployments in one of the following states: Deployed as available, deployment disabled, start time or deadline in the future
35 | 1. Changed the column "Missing updated approved" to only show missing updates if the corresponding deployment has not been filtered out via the new exclude parameter. The "per device" report still shows all updates no matter the deployment selection
36 | 1. Added the new deployment exclude parameter also to the "Per device deployments" report. The report will now exclude deployments based on the parameter.
37 | 1. Added cumulative update prefix like "2022-04" to the dashboard for each rollup bar graph
38 | 1. Added systems domain name column to each list report
39 | 1. Added switch "TryOverwrite" to import script. If set, the script will try to overwrite existing report items. Might not work in every case. If successful subscriptions will also be kept.
40 | 1. Added new filter to the "Per device" report called: "All missing Security and Critical updates deployed or not"
41 | 1. Removed Security Update requirement in QFE query to improve QFE detection accuracy
42 | 1. Fixed typo in "Per device" report based on: https://github.com/jonasatgit/updatereporting/issues/9
43 | 1. Fixes typo in import script
44 | 1. Fixed sorting issue in "per device" report
45 | 1. Fixed sorting issue in "per device deployments" report
46 | 1. Fixed "uncompliant" typo in "compliance list" report via: https://github.com/jonasatgit/updatereporting/pull/15
47 | 1. Fixed typo in "compare update compliance" via: https://github.com/jonasatgit/updatereporting/pull/14
48 | 1. Fixed an issue with parameters not correctly handled between the dashboard and most of the sub-reports
49 |
50 | ## 2021-11-18 v3.6:
51 | 1. Added "Cumulative Update for Microsoft server operating system" string for server 2022 updates
52 |
53 | ## 2021-07-02 v3.5:
54 | 1. Changed the overall compliance state from "all approved and missing updates" + "a security update installation happend within one month" to "All deployments are compliant" + "either the last or the current cumulative update is installed" + "a security update installation happend within one month"
55 | 1. Added help text to all report column headers
56 | 1. Added Update install errors bar graph to dashboard (below WSUS scan errors)
57 | 1. Changed filter for top 10 systems on dashboard to be more accurate
58 | 1. Added top 10 update install errors to dashboard
59 | 1. Added new report with details about install errors and WSUS scan errors
60 | 1. Contains around 400 common windows update related errors with possible actions on how to fix them
61 | 1. Added new parameter to exclude deployments containing Microsoft Defender and System Center Endpoint Protection updates
62 | 1. Was previously part of the SQL query and not easily changeable nor visible to the report user
63 | 1. Removed Server 2008 specific parts
64 | 1. Added new filter to "per device" report called: "Missing updates with errors" and “All missing updates deployed or not”
65 | 1. Added more details about errors to "per device" report
66 | 1. Added update collection and maintenance window list to “per device” report
67 | 1. Added column: “Earliest Deadline” to “per device” report
68 | 1. Changed first sub-report name from “all uncompliant” to “compliance list”
69 | 1. Changed default sort order from "count of missing updates" to "month since last update install"
70 | 1. Changed "WSUS version" to "OS build version". Easier to determine actual OS version and patch level
71 | 1. Changed "Defender Pattern Version" to "Defender Pattern Age" to be able to spot systems with older pattern more easily
72 | 1. Added column "WSUS scan error" to system list
73 | 1. Added column count of "Updates with install error" to system list
74 | 1. Added column number of "Deployments non compliant" to system list
75 | 1. Helps to determine any problems with deployments when all updates are installed, but deployments are still marked as uncompliant
76 | 1. Added new report to list all update deployments and their states per device
77 | 1. Made "Per device" and “compliance list" report visible to be able to schedule subscriptions without the dashboard
78 | 1. Fixed several minor issues with each report
79 | 1. Changed SQL query for deployed updates to work better in larger environments
80 | 1. Changed import script to also handle SSRS folder path with spaces in it
81 | 1. Changed import script to delete existing contents of "work" folder from a previous run
82 | 1. Changed import script parameter name "Upload" to "DoNotUpload". Function is the same.
83 | 1. Removed import script parameter "UseViewForDataset". (To much work to keep the view consistent with regular query)
84 | 1. Added new import script parameters: "ForceLegacyCardinalitySQL2016SP1AndHigher" and "ForceLegacyCardinalityOlderThanSQL2016SP1" Read more about it here
85 |
86 | ## 2020-12-09 v2.1:
87 | 1. Fixed language and QFE problem
88 | 1. Added new parameter -ForceLegacyFormat,
89 | 1. Fixed minor issues and linked all reports to the per device sub-report
90 |
91 | ## 2020-11-03 v1.0:
92 | 1. Fixed wrong parameter name, updated repository with several fixes
93 |
--------------------------------------------------------------------------------
/SourceFiles/Compare Update Compliance.rdl:
--------------------------------------------------------------------------------
1 |
2 |
3 | Comparison of a maximum of six systems and their update compliance statuses
4 | 0
5 |
6 |
7 | /ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}
8 | None
9 | 7f594453-7be9-46cf-aa6c-e4065a7931e2
10 |
11 |
12 |
13 |
14 |
15 | DataSource1
16 |
17 |
18 | =Parameters!onlyRegUpdates.Value
19 |
20 |
21 | =Parameters!Systems.Value
22 |
23 |
24 | IF @onlyRegUpdates = 1
25 |
26 | With SystemsLimited_CTE (ResourceID)
27 | AS
28 | (
29 | select top 6 ResourceID from v_R_System T00 where T00.ResourceID in (@Systems)
30 |
31 | )
32 |
33 | select SYS.Name0,
34 | concat(OS.caption0, ' ' ,OS.CSDVersion0) as OSystem,
35 | ui.Title,
36 | Targeted=(case when ctm.ResourceID is not null then 'X' end),
37 | Installed=(case when css.Status=3 then 'X' end),
38 | IsRequired=(case when css.Status=2 then 'X' end)
39 | from v_Update_ComplianceStatus css
40 | join v_R_System SYS on SYS.ResourceID = css.ResourceID
41 | join v_UpdateInfo ui on ui.CI_ID=css.CI_ID
42 | join v_CICategoryInfo_All vnd on vnd.CI_ID=ui.CI_ID and vnd.CategoryTypeName='Company'
43 | join v_CICategoryInfo_All cls on cls.CI_ID=ui.CI_ID and cls.CategoryTypeName='UpdateClassification'
44 | left join v_CITargetedMachines ctm on ctm.CI_ID=css.CI_ID and ctm.ResourceID = css.ResourceID
45 | left join v_GS_OPERATING_SYSTEM OS on OS.ResourceID = CSS.ResourceID
46 | where css.ResourceID in (select ResourceID from SystemsLimited_CTE)
47 | and css.Status=2
48 | order by SYS.name0
49 |
50 | ELSE
51 |
52 | With SystemsLimited_CTE (ResourceID)
53 | AS
54 | (
55 | select top 6 ResourceID from v_R_System T00 where T00.ResourceID in (@Systems)
56 |
57 | )
58 | select SYS.Name0,
59 | concat(OS.caption0, ' ' ,OS.CSDVersion0) as OSystem,
60 | ui.Title,
61 | Targeted=(case when ctm.ResourceID is not null then 'X' end),
62 | Installed=(case when css.Status=3 then 'X' end),
63 | IsRequired=(case when css.Status=2 then 'X' end)
64 | from v_Update_ComplianceStatus css
65 | join v_R_System SYS on SYS.ResourceID = css.ResourceID
66 | join v_UpdateInfo ui on ui.CI_ID=css.CI_ID
67 | join v_CICategoryInfo_All vnd on vnd.CI_ID=ui.CI_ID and vnd.CategoryTypeName='Company'
68 | join v_CICategoryInfo_All cls on cls.CI_ID=ui.CI_ID and cls.CategoryTypeName='UpdateClassification'
69 | left join v_CITargetedMachines ctm on ctm.CI_ID=css.CI_ID and ctm.ResourceID = css.ResourceID
70 | left join v_GS_OPERATING_SYSTEM OS on OS.ResourceID = CSS.ResourceID
71 | where css.ResourceID in (select ResourceID from SystemsLimited_CTE)
72 | order by SYS.name0
73 | true
74 |
75 |
76 |
77 | Name0
78 | System.String
79 |
80 |
81 | Title
82 | System.String
83 |
84 |
85 | OSystem
86 | System.String
87 |
88 |
89 | Targeted
90 | System.String
91 |
92 |
93 | Installed
94 | System.String
95 |
96 |
97 | IsRequired
98 | System.String
99 |
100 |
101 |
102 |
103 |
104 | DataSource1
105 |
106 |
107 | =Parameters!Search.Value
108 |
109 |
110 | select Name0, ResourceID
111 | from v_R_System T00
112 | where T00.Client0 = 1
113 | and T00.Decommissioned0 !=1
114 | and T00.Obsolete0 !=1
115 | and T00.Name0 like @Search
116 | order by Name0
117 | true
118 |
119 |
120 |
121 | Name0
122 | System.String
123 |
124 |
125 | ResourceID
126 | System.Int32
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | true
143 | true
144 |
145 |
146 |
147 |
148 | Choose systems that are equal related to their operating system and installed roles.
149 |
150 |
151 |
152 |
153 |
154 |
155 | The report will only show applicable updates to the specific system and not a list of all updates available.
156 |
157 |
158 |
159 |
160 | Textbox13
161 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | 2pt
176 | 2pt
177 | 2pt
178 | 2pt
179 |
180 |
181 | 3
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | true
196 | true
197 |
198 |
199 |
200 |
201 | Update-Title
202 |
207 |
208 |
209 |
210 |
211 | Textbox18
212 |
216 |
217 | Silver
218 | 2pt
219 | 2pt
220 | 2pt
221 | 2pt
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | 0.45833in
233 |
234 |
235 | 0.5in
236 |
237 |
238 | 0.41667in
239 |
240 |
241 |
242 |
243 | 0.25in
244 |
245 |
246 |
247 |
248 | true
249 | true
250 |
251 |
252 |
253 |
254 | =Fields!Installed.Value
255 |
260 |
261 |
262 |
265 |
266 |
267 | Installed1
268 |
272 |
273 |
274 | Gray
275 | 2pt
276 |
277 | Middle
278 | 2pt
279 | 2pt
280 | 2pt
281 | 2pt
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 | true
290 | true
291 |
292 |
293 |
294 |
295 | =Fields!Targeted.Value
296 |
301 |
302 |
303 |
306 |
307 |
308 | Targeted
309 |
313 |
314 | Middle
315 | 2pt
316 | 2pt
317 | 2pt
318 | 2pt
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 | true
327 | true
328 |
329 |
330 |
331 |
332 | =Fields!IsRequired.Value
333 |
338 |
339 |
340 |
343 |
344 |
345 | IsRequired
346 |
350 |
351 |
352 | Gray
353 | 2pt
354 |
355 | Middle
356 | 2pt
357 | 2pt
358 | 2pt
359 | 2pt
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 | =Fields!Name0.Value
374 |
375 |
376 |
377 |
378 | =Fields!Name0.Value
379 |
380 |
381 |
382 | 0.25in
383 |
384 |
385 | true
386 | true
387 |
388 |
389 |
390 |
391 | =Fields!Name0.Value
392 |
396 |
397 |
398 |
399 |
400 | Name0
401 |
405 |
406 |
407 | Gray
408 | 2pt
409 |
410 |
411 | Gray
412 | 2pt
413 |
414 | LightGrey
415 | 2pt
416 | 2pt
417 | 2pt
418 | 2pt
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 | 0.25in
427 |
428 |
429 | true
430 | true
431 |
432 |
433 |
434 |
435 | =Fields!OSystem.Value
436 |
440 |
441 |
442 |
443 |
444 | OSystem
445 |
449 |
450 |
451 | Gray
452 | 2pt
453 |
454 |
455 | Gray
456 | 2pt
457 |
458 | LightGrey
459 | 2pt
460 | 2pt
461 | 2pt
462 | 2pt
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 | 0.25in
471 |
472 |
473 | true
474 | true
475 |
476 |
477 |
478 |
479 | Inst
480 |
486 |
487 |
488 |
489 |
490 | Textbox5
491 | X to indicate an installed update
492 |
496 |
497 |
498 | Gray
499 | 2pt
500 |
501 | #4c68a2
502 | 2pt
503 | 2pt
504 | 2pt
505 | 2pt
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 | 0.25in
514 |
515 |
516 | true
517 | true
518 |
519 |
520 |
521 |
522 | =Count(Fields!Installed.Value)
523 |
528 |
529 |
530 |
533 |
534 |
535 | Textbox19
536 | Count of installed updates
537 |
541 |
542 |
543 | Gray
544 | 2pt
545 |
546 | Silver
547 | Middle
548 | 2pt
549 | 2pt
550 | 2pt
551 | 2pt
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 | 0.25in
562 |
563 |
564 | true
565 | true
566 |
567 |
568 |
569 |
570 | Targ
571 |
577 |
578 |
579 |
580 |
581 | Textbox8
582 | X to indicate a deployed / targeted update
583 |
587 |
588 | #4c68a2
589 | 2pt
590 | 2pt
591 | 2pt
592 | 2pt
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 | 0.25in
601 |
602 |
603 | true
604 | true
605 |
606 |
607 |
608 |
609 | =Count(Fields!Targeted.Value)
610 |
615 |
616 |
617 |
620 |
621 |
622 | Textbox22
623 | Count of deployed / targeted updates
624 |
628 |
629 | Silver
630 | Middle
631 | 2pt
632 | 2pt
633 | 2pt
634 | 2pt
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 | 0.25in
645 |
646 |
647 | true
648 | true
649 |
650 |
651 |
652 |
653 | Req
654 |
660 |
661 |
662 |
663 |
664 | Textbox11
665 | X to indicate a required / missing update
666 |
670 |
671 |
672 | Gray
673 | 2pt
674 |
675 | #4c68a2
676 | 2pt
677 | 2pt
678 | 2pt
679 | 2pt
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 | 0.25in
688 |
689 |
690 | true
691 | true
692 |
693 |
694 |
695 |
696 | =Count(Fields!IsRequired.Value)
697 |
701 |
702 |
703 |
706 |
707 |
708 | Textbox21
709 | Count of required / missing updates
710 |
714 |
715 |
716 | Gray
717 | 2pt
718 |
719 | Silver
720 | Middle
721 | 2pt
722 | 2pt
723 | 2pt
724 | 2pt
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 | =Fields!Title.Value
744 |
745 |
746 |
747 |
748 | =Fields!Title.Value
749 |
750 |
751 |
752 | 4.95833in
753 |
754 |
755 | true
756 | true
757 |
758 |
759 |
760 |
761 | =Fields!Title.Value
762 |
765 |
766 |
767 |
768 |
769 | Title
770 |
774 |
775 | 2pt
776 | 2pt
777 | 2pt
778 | 2pt
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 | Compare
790 | 0in
791 | 0in
792 | 1.25in
793 | 6.33333in
794 |
797 |
798 |
799 |
800 |
801 | 4.51736in
802 |
803 | 19.82292in
804 |
805 |
806 | 0.27084in
807 | true
808 | true
809 |
812 |
813 |
814 |
815 |
816 | 0.01944in
817 | true
818 | true
819 |
822 |
823 |
824 |
825 | 1in
826 | 1in
827 | 1in
828 | 1in
829 |
830 |
831 |
832 |
833 |
834 | String
835 |
836 |
837 | =User!Language
838 |
839 |
840 | true
841 |
842 |
843 | String
844 |
845 |
846 | %
847 |
848 |
849 | Limit list of systems: (wildcard %):
850 |
851 |
852 | String
853 | Choose Systems (max 6):
854 |
855 |
856 | Systems
857 | ResourceID
858 | Name0
859 |
860 |
861 | true
862 |
863 |
864 | Integer
865 |
866 |
867 | 0
868 |
869 |
870 | Show only required updates
871 |
872 |
873 |
874 | 1
875 |
876 |
877 |
878 | 0
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 | 2
888 | 3
889 |
890 |
891 | 0
892 | 0
893 | locale
894 |
895 |
896 | 0
897 | 1
898 | Search
899 |
900 |
901 | 1
902 | 1
903 | Systems
904 |
905 |
906 | 0
907 | 2
908 | onlyRegUpdates
909 |
910 |
911 |
912 |
913 | =User!Language
914 |
915 | SrsResources, culture=neutral
916 |
917 | true
918 | Inch
919 | http://reportserver.domain.local/ReportServer
920 | f3e5ae98-66c5-4863-b1e2-69153dbb97e3
921 |
922 |
--------------------------------------------------------------------------------
/SourceFiles/Software Updates Compliance - ErrorList.rdl:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
5 |
6 | /ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}
7 | None
8 | 7f594453-7be9-46cf-aa6c-e4065a7931e2
9 |
10 |
11 |
12 |
13 |
14 | UpdatesInstallErrorList
15 |
16 |
17 | =Parameters!ReportType.Value
18 |
19 |
20 | =Parameters!CollectionIDs.Value
21 |
22 |
23 | =Parameters!CI_ID.Value
24 |
25 |
26 | =Parameters!LastErrorCode.Value
27 |
28 |
29 | http://reportserver.domain.local/reportserver
30 |
31 |
32 |
33 | Title
34 | true
35 |
36 |
37 | Name
38 | true
39 |
40 |
41 | LastErrorCode
42 | true
43 |
44 |
45 | ErrorDescription
46 | true
47 |
48 |
49 | Action
50 | true
51 |
52 |
53 | InfoURL
54 | true
55 |
56 |
57 |
58 |
59 |
60 | DataSource1
61 |
62 |
63 | =Parameters!CI_ID.Value
64 |
65 |
66 | Select Title, InfoURL from v_UpdateInfo where CI_ID = @CI_ID
67 | true
68 |
69 |
70 |
71 | Title
72 | System.String
73 |
74 |
75 | InfoURL
76 | System.String
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | 2.25in
90 |
91 |
92 | 1.69167in
93 |
94 |
95 | 4.4in
96 |
97 |
98 | 11.35833in
99 |
100 |
101 | 0.85833in
102 |
103 |
104 |
105 |
106 | 0.25in
107 |
108 |
109 |
110 |
111 | true
112 |
113 | =Fields!Name.Value
114 |
115 | true
116 |
117 |
118 |
119 |
120 | Name
121 |
126 |
127 |
128 |
131 |
132 |
133 | Textbox2
134 | Name of computer
135 |
139 |
140 |
141 |
142 | 1.5pt
143 |
144 |
145 |
146 | 1.5pt
147 |
148 | White
149 | Bottom
150 | 5pt
151 | 5pt
152 | 5pt
153 | 5pt
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | true
162 |
163 | =Fields!LastErrorCode.Value
164 |
165 | true
166 |
167 |
168 |
169 |
170 | ErrorCode
171 |
176 |
177 |
178 |
181 |
182 |
183 | Last error code
184 |
188 |
189 |
190 |
191 | 1.5pt
192 |
193 |
194 |
195 | 1.5pt
196 |
197 | White
198 | Bottom
199 | 5pt
200 | 5pt
201 | 5pt
202 | 5pt
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | true
211 |
212 | =Fields!ErrorDescription.Value
213 |
214 | true
215 |
216 |
217 |
218 |
219 | Error Description
220 |
225 |
226 |
227 |
230 |
231 |
232 | Error description
233 |
237 |
238 |
239 |
240 | 1.5pt
241 |
242 |
243 |
244 | 1.5pt
245 |
246 | White
247 | Bottom
248 | 5pt
249 | 5pt
250 | 5pt
251 | 5pt
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 | true
260 |
261 | =Fields!Action.Value
262 |
263 | true
264 |
265 |
266 |
267 |
268 | Possible Action (might not work in every case)
269 |
274 |
275 |
276 |
279 |
280 |
281 | A possible action to fix the error based on different Microsoft docs articles or Customer Engineer experience. The possible solution might not help in every case, but should give you a starting point to fix the underlaying issue.
282 |
286 |
287 |
288 |
289 | 1.5pt
290 |
291 |
292 |
293 | 1.5pt
294 |
295 | White
296 | Bottom
297 | 5pt
298 | 5pt
299 | 5pt
300 | 5pt
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 | true
309 |
310 | =Fields!InfoURL.Value
311 |
312 | true
313 |
314 |
315 |
316 |
317 | InfoURL
318 |
323 |
324 |
325 |
328 |
329 |
330 | A link to a docs article describing the error or the solution in more detail
331 |
335 |
336 |
337 |
338 | 1.5pt
339 |
340 |
341 |
342 | 1.5pt
343 |
344 | White
345 | Bottom
346 | 5pt
347 | 5pt
348 | 5pt
349 | 5pt
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 | 0.25in
358 |
359 |
360 |
361 |
362 | true
363 | true
364 |
365 |
366 |
367 |
368 | =Fields!Name.Value
369 |
374 |
375 |
376 |
379 |
380 |
381 | Name
382 |
383 |
384 |
385 |
386 | Software Updates Compliance - Per device
387 |
388 |
389 | =IIf(Parameters!ReportType.Value = 4,0,3)
390 |
391 |
392 | =Fields!Name.Value
393 |
394 |
395 |
396 |
397 |
398 |
399 |
402 |
403 |
404 | Silver
405 |
406 |
407 | =iif(RowNumber(Nothing) Mod 2, "Transparent", "Gainsboro")
408 | Top
409 | 5pt
410 | 5pt
411 | 5pt
412 | 5pt
413 |
414 |
415 | true
416 |
417 |
418 |
419 |
420 |
421 | true
422 | true
423 |
424 |
425 |
426 |
427 | ="0x" & Hex(Fields!LastErrorCode.Value)
428 |
433 |
434 |
435 |
438 |
439 |
440 |
443 |
444 |
445 | Silver
446 |
447 |
448 | =iif(RowNumber(Nothing) Mod 2, "Transparent", "Gainsboro")
449 | Top
450 | 5pt
451 | 5pt
452 | 5pt
453 | 5pt
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 | true
462 | true
463 |
464 |
465 |
466 |
467 | =Fields!ErrorDescription.Value
468 |
473 |
474 |
475 |
478 |
479 |
480 |
483 |
484 |
485 | Silver
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 | =iif(RowNumber(Nothing) Mod 2, "Transparent", "Gainsboro")
495 | Top
496 | 5pt
497 | 5pt
498 | 5pt
499 | 5pt
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 | true
508 | true
509 |
510 |
511 |
512 |
513 | =Fields!Action.Value
514 |
519 |
520 |
521 |
524 |
525 |
526 |
529 |
530 |
531 | Silver
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 | =iif(RowNumber(Nothing) Mod 2, "Transparent", "Gainsboro")
541 | Top
542 | 5pt
543 | 5pt
544 | 5pt
545 | 5pt
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 | true
554 | true
555 |
556 |
557 |
558 |
559 | =IIf(Fields!InfoURL.Value="","","Link")
560 |
565 |
566 |
567 |
570 |
571 |
572 |
573 |
574 |
575 | =Fields!InfoURL.Value
576 |
577 |
578 |
579 |
582 |
583 |
584 | Silver
585 |
586 |
587 | =iif(RowNumber(Nothing) Mod 2, "Transparent", "Gainsboro")
588 | Top
589 | 5pt
590 | 5pt
591 | 5pt
592 | 5pt
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 | After
614 | true
615 | true
616 |
617 |
618 |
619 | Detail
620 |
621 |
622 |
623 |
624 | Detail_Collection
625 | Output
626 | true
627 |
628 |
629 |
630 | UpdatesInstallErrorList
631 | 0.36111in
632 | 0in
633 | 0.5in
634 | 20.55833in
635 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 | Tahoma
652 | 8pt
653 | Normal
654 | None
655 | General
656 | Top
657 |
658 |
659 |
660 | true
661 | true
662 |
663 |
664 |
665 |
666 | =Parameters!ReportType.Label
667 |
670 |
671 |
672 |
675 |
676 |
677 | Textbox1
678 | 0.11333in
679 | 0in
680 | 0.24778in
681 | 5.80667in
682 | 1
683 |
686 |
687 | 2pt
688 | 2pt
689 | 2pt
690 | 2pt
691 |
692 |
693 |
694 | true
695 | true
696 |
697 |
698 |
699 |
700 | =IIf(Parameters!ReportType.Value=1,First(Fields!Title.Value, "UpdateTitle"),"")
701 |
704 |
705 |
706 |
709 |
710 |
711 | Textbox1
712 |
713 |
714 |
715 | =First(Fields!InfoURL.Value, "UpdateTitle")
716 |
717 |
718 |
719 | 0.11333in
720 | 5.94125in
721 | 0.24778in
722 | 13.18167in
723 | 2
724 |
727 |
728 | 2pt
729 | 2pt
730 | 2pt
731 | 2pt
732 |
733 |
734 |
735 | 1.03819in
736 |
737 | 21.0725in
738 |
739 |
740 | 0.01042in
741 | true
742 | true
743 |
746 |
747 |
748 |
749 |
750 | 0.01944in
751 | true
752 | true
753 |
756 |
757 |
758 |
759 | 1in
760 | 1in
761 | 1in
762 | 1in
763 |
764 |
765 |
766 |
767 |
768 | Integer
769 | Report Type
770 |
771 |
772 |
773 | 1
774 |
775 |
776 |
777 | 2
778 |
779 |
780 |
781 | 3
782 |
783 |
784 |
785 | 4
786 |
787 |
788 |
789 | 5
790 |
791 |
792 |
793 |
794 |
795 |
796 | String
797 | Collection IDs
798 | true
799 |
800 |
801 | Integer
802 | Update CI_ID (set to 0 if errorcode is set)
803 |
804 |
805 | Integer
806 | ErrorCode (set to 0 if update ID is set)
807 |
808 |
809 |
810 |
811 | 2
812 | 3
813 |
814 |
815 | 0
816 | 1
817 | ReportType
818 |
819 |
820 | 1
821 | 1
822 | CollectionIDs
823 |
824 |
825 | 0
826 | 2
827 | CI_ID
828 |
829 |
830 | 1
831 | 2
832 | LastErrorCode
833 |
834 |
835 |
836 |
837 | =User!Language
838 |
839 | SrsResources, culture=neutral
840 |
841 | true
842 | Inch
843 | http://reportserver.domain.local/reportserver
844 | f3e5ae98-66c5-4863-b1e2-69153dbb97e3
845 |
846 |
--------------------------------------------------------------------------------
/SourceFiles/Software Updates Compliance - Overview Updates missing not deployed.rdl:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
5 |
6 | /ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}
7 | None
8 | 66169a94-0965-431f-8bdb-6ff7fbdeb71b
9 |
10 |
11 |
12 |
13 |
14 | DataSource1
15 |
16 |
17 | =Parameters!SupersededVisible.Value
18 |
19 |
20 | =Parameters!CollectionIDs.Value
21 |
22 |
23 | IF @SupersededVisible = 'Yes'
24 | select UPD.Title
25 | ,VRS.Netbios_Name0
26 | ,VRS.ResourceID
27 | ,CCI.CategoryInstanceName
28 | ,UCS.CI_ID
29 | ,UPD.IsSuperseded
30 | ,UPD.DatePosted
31 | ,count(UCS.CI_ID) over(partition by UCS.CI_ID) as [SystemsWithSameUpdate]
32 | from v_UpdateComplianceStatus UCS
33 | left join v_CITargetedMachines CTM on CTM.CI_ID = UCS.CI_ID and CTM.ResourceID = UCS.ResourceID
34 | inner join v_CICategoryInfo_All CCI on CCI.CI_ID = UCS.CI_ID and CCI.CategoryTypeName = 'UpdateClassification' and (CCI.CategoryInstanceName = 'Security Updates' or CCI.CategoryInstanceName = 'Critical Updates')
35 | inner join v_UpdateInfo UPD on UPD.CI_ID = UCS.CI_ID
36 | inner join v_R_System VRS on VRS.ResourceID = UCS.ResourceID
37 | where UCS.ResourceID IN (Select ResourceID from v_FullCollectionMembership fcm where fcm.CollectionID IN (@CollectionIDs))
38 | and UCS.Status = 2 and CTM.ResourceID is null --and UPD.IsSuperseded = 0
39 | order by SystemsWithSameUpdate desc, UPD.Title, UPD.DatePosted
40 | ---- fix for SQL compat level problem
41 | ---- https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
42 | ---- Force legacy cardinality for SQL server versions before 2016 SP1 (SQL version less than 13.0.4001.0)
43 | --OPTION (QUERYTRACEON 9481)
44 | ---- Force legacy cardinality for SQL server versions 2016 SP1 and higher (SQL version equal or greater than 13.0.4001.0)
45 | --OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))
46 | ELSE
47 | select UPD.Title
48 | ,VRS.Netbios_Name0
49 | ,VRS.ResourceID
50 | ,CCI.CategoryInstanceName
51 | ,UCS.CI_ID
52 | ,UPD.IsSuperseded
53 | ,UPD.DatePosted
54 | ,count(UCS.CI_ID) over(partition by UCS.CI_ID) as [SystemsWithSameUpdate]
55 | from v_UpdateComplianceStatus UCS
56 | left join v_CITargetedMachines CTM on CTM.CI_ID = UCS.CI_ID and CTM.ResourceID = UCS.ResourceID
57 | inner join v_CICategoryInfo_All CCI on CCI.CI_ID = UCS.CI_ID and CCI.CategoryTypeName = 'UpdateClassification' and (CCI.CategoryInstanceName = 'Security Updates' or CCI.CategoryInstanceName = 'Critical Updates')
58 | inner join v_UpdateInfo UPD on UPD.CI_ID = UCS.CI_ID
59 | inner join v_R_System VRS on VRS.ResourceID = UCS.ResourceID
60 | where UCS.ResourceID IN (Select ResourceID from v_FullCollectionMembership fcm where fcm.CollectionID IN (@CollectionIDs))
61 | and UCS.Status = 2 and CTM.ResourceID is null and UPD.IsSuperseded = 0
62 | order by SystemsWithSameUpdate desc, UPD.Title, UPD.DatePosted
63 | ---- fix for SQL compat level problem
64 | ---- https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
65 | ---- Force legacy cardinality for SQL server versions before 2016 SP1 (SQL version less than 13.0.4001.0)
66 | --OPTION (QUERYTRACEON 9481)
67 | ---- Force legacy cardinality for SQL server versions 2016 SP1 and higher (SQL version equal or greater than 13.0.4001.0)
68 | --OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))
69 | true
70 |
71 |
72 |
73 | Title
74 | System.String
75 |
76 |
77 | Netbios_Name0
78 | System.String
79 |
80 |
81 | ResourceID
82 | System.Int32
83 |
84 |
85 | CategoryInstanceName
86 | System.String
87 |
88 |
89 | CI_ID
90 | System.Int32
91 |
92 |
93 | IsSuperseded
94 | System.Int32
95 |
96 |
97 | DatePosted
98 | System.DateTime
99 |
100 |
101 | SystemsWithSameUpdate
102 | System.Int32
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | 86.29515mm
116 |
117 |
118 | 37.34722mm
119 |
120 |
121 | 25mm
122 |
123 |
124 |
125 |
126 | 6mm
127 |
128 |
129 |
130 |
131 | true
132 | true
133 |
134 |
135 |
136 |
137 | ComputerName
138 |
141 |
142 |
143 |
144 |
145 | Textbox149
146 |
150 |
151 | 2pt
152 | 2pt
153 | 2pt
154 | 2pt
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | true
163 | true
164 |
165 |
166 |
167 |
168 | Date Posted
169 |
172 |
173 |
174 |
175 |
176 |
177 | Textbox150
178 |
182 |
183 | 2pt
184 | 2pt
185 | 2pt
186 | 2pt
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | true
195 | true
196 |
197 |
198 |
199 |
200 | Superseded
201 |
204 |
205 |
206 |
207 |
208 |
209 | Textbox168
210 |
214 |
215 | 2pt
216 | 2pt
217 | 2pt
218 | 2pt
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 | 6mm
227 |
228 |
229 |
230 |
231 | true
232 | true
233 |
234 |
235 |
236 |
237 | ="Systems with same update missing: " & First(Fields!SystemsWithSameUpdate.Value)
238 |
239 |
240 |
241 |
242 | SystemsWithSameUpdate1
243 |
247 |
248 | LightGrey
249 | 2pt
250 | 2pt
251 | 2pt
252 | 2pt
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 | true
261 | true
262 |
263 |
264 |
265 |
266 |
267 |
270 |
271 |
272 |
273 |
274 |
275 | Textbox159
276 |
280 |
281 | 2pt
282 | 2pt
283 | 2pt
284 | 2pt
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 | true
293 | true
294 |
295 |
296 |
297 |
298 |
299 |
302 |
303 |
304 |
305 |
306 | Textbox169
307 |
311 |
312 | 2pt
313 | 2pt
314 | 2pt
315 | 2pt
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 | 6mm
324 |
325 |
326 |
327 |
328 | true
329 | true
330 |
331 |
332 |
333 |
334 | =Fields!Netbios_Name0.Value
335 |
338 |
339 |
340 |
341 |
342 | Netbios_Name03
343 |
344 |
345 |
346 |
347 | Software Updates Compliance - Per device
348 |
349 |
350 | 0
351 |
352 |
353 | =Fields!Netbios_Name0.Value
354 |
355 |
356 |
357 |
358 |
359 |
360 |
364 |
365 | 2pt
366 | 2pt
367 | 2pt
368 | 2pt
369 |
370 |
371 | true
372 |
373 |
374 |
375 |
376 |
377 | true
378 | true
379 |
380 |
381 |
382 |
383 | =Fields!DatePosted.Value
384 |
387 |
388 |
389 |
390 |
391 |
392 | DatePosted3
393 |
397 |
398 | 2pt
399 | 2pt
400 | 2pt
401 | 2pt
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 | true
410 | true
411 |
412 |
413 |
414 |
415 | =IIf(Fields!IsSuperseded.Value = 1, "Yes", "No")
416 |
417 |
418 |
419 |
420 |
421 |
422 | IsSuperseded
423 |
427 |
428 | 2pt
429 | 2pt
430 | 2pt
431 | 2pt
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 | 271.38054mm
452 |
453 |
454 | true
455 | true
456 |
457 |
458 |
459 |
460 | Update
461 |
464 |
465 |
466 |
467 |
468 |
469 | Textbox152
470 |
474 |
475 | 2pt
476 | 2pt
477 | 2pt
478 | 2pt
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 | After
487 |
488 |
489 |
490 |
491 | =Fields!Title.Value
492 |
493 |
494 |
495 |
496 | =Fields!SystemsWithSameUpdate.Value
497 | Descending
498 |
499 |
500 |
501 | 271.38054mm
502 |
503 |
504 | true
505 | true
506 |
507 |
508 |
509 |
510 | =Fields!Title.Value
511 |
512 |
513 |
514 |
515 |
516 |
517 | Title2
518 |
522 |
523 | LightGrey
524 | 2pt
525 | 2pt
526 | 2pt
527 | 2pt
528 |
529 |
530 |
531 |
532 |
533 |
534 | After
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 | true
543 | Title2
544 |
545 |
546 |
547 |
548 |
549 |
550 | DataSet1
551 | 23.8125mm
552 | 10.55158mm
553 | 18mm
554 | 420.02291mm
555 |
558 |
559 |
560 |
561 |
562 | true
563 | true
564 |
565 |
566 |
567 |
568 | Systems with missing security updates not yet approved
569 |
573 |
574 |
575 |
578 |
579 |
580 | Textbox7
581 | 6.05366mm
582 | 10.55158mm
583 | 9.75783mm
584 | 420.02291mm
585 | 1
586 |
589 |
590 | 2pt
591 | 2pt
592 | 2pt
593 | 2pt
594 |
595 |
596 |
597 | true
598 | true
599 |
600 |
601 |
602 |
603 | (Superseded can be filtered above)
604 |
605 |
606 |
609 |
610 |
611 | Textbox1
612 | 16.16427mm
613 | 10.55158mm
614 | 5.85258mm
615 | 420.02291mm
616 | 2
617 |
620 |
621 | Top
622 | 2pt
623 | 2pt
624 | 2pt
625 | 2pt
626 |
627 |
628 |
629 | 49.70993mm
630 |
633 |
634 |
635 |
636 | 681.87034mm
637 |
638 | 297mm
639 | 210mm
640 | 20mm
641 | 20mm
642 | 20mm
643 | 20mm
644 | 0.13cm
645 |
646 |
647 |
648 |
649 |
650 | String
651 |
652 |
653 | SMS00001
654 |
655 |
656 | CollectionIDs
657 | true
658 | true
659 |
660 |
661 | String
662 |
663 |
664 | Yes
665 |
666 |
667 | Also show superseded updates
668 |
669 |
670 |
671 | Yes
672 |
673 |
674 |
675 | No
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 | 1
685 | 2
686 |
687 |
688 | 0
689 | 0
690 | CollectionIDs
691 |
692 |
693 | 0
694 | 1
695 | SupersededVisible
696 |
697 |
698 |
699 |
700 | Mm
701 | http://reportserver.domain.local/reportserver
702 | 8649b1b8-cc33-4ef7-998b-ae4c41462811
703 |
704 |
--------------------------------------------------------------------------------
/SourceFiles/UpdatesSummary.rsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | /ConfigMgr_P11/{5C6358F2-4BB6-4a1b-A16E-8D96795D8602}
6 |
7 |
8 | false
9 | false
10 | false
11 | String
12 |
13 |
14 | false
15 | false
16 | false
17 | String
18 |
19 |
20 | false
21 | false
22 | false
23 | Int32
24 |
25 |
26 | false
27 | false
28 | false
29 | String
30 |
31 |
32 | ---SCRIPTVERSION: 20240506
33 |
34 | -----------------------------------------------------------------------------------------------------------------------
35 | ---- Disclaimer
36 | ----
37 | ---- This sample script is not supported under any Microsoft standard support program or service. This sample
38 | ---- script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties
39 | ---- including, without limitation, any implied warranties of merchantability or of fitness for a particular
40 | ---- purpose. The entire risk arising out of the use or performance of this sample script and documentation
41 | ---- remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation,
42 | ---- production, or delivery of this script be liable for any damages whatsoever (including, without limitation,
43 | ---- damages for loss of business profits, business interruption, loss of business information, or other
44 | ---- pecuniary loss) arising out of the use of or inability to use this sample script or documentation, even
45 | ---- if Microsoft has been advised of the possibility of such damages.
46 | -----------------------------------------------------------------------------------------------------------------------
47 | ---- Changelog:
48 | ---- 2024-05-06: Changed update compliance definition and removed "and (DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE()) <= 1+@MonthIndexInternal)"
49 | ---- 2022-04-04: Changed the way deployed updates are shown. "MissingUpdatesApproved" will not show updates for excluded deployments anymore. Makes it more consistant with the rest of the report
50 | ---- Added systems domain name to the list
51 | ---- Removed unnecessary where clause: "where (QFE.Description0 = 'Security Update' or QFE.Description0 is null)"
52 | ---- Added Parameter @MonthIndex to be able to set the report one month back. Only possible if updates are delayed for one month and not deployed within the month of their release date
53 | ---- Added Paremeter to be able exclude future deployments based on starttime or deadline, exclude available and diabled deployments
54 | ---- 2022-03-02: Fixed @SecondTuesdayOfMonth and change logic for pulling the current and last rollups. Based on pull request 11
55 | ---- 2021-11-18: Added "Cumulative Update for Microsoft server operating system" string for server 2022 updates.
56 | ---- 2021-08-01: Changed multi value parameter handling to use CTEs due to performance issues with some environments.
57 | ---- Also changed overall compliance to be only compliant in case the last rollup has been installed and changed name from "UpdateAssignmentCompliance" to "OverallComplianceState"
58 | ---- Changed name from "UpdateStatusCompliant" to "UpdateAssignmentCompliant" to be more accurate with the wording
59 | ---- Removed condition "UPDATESTATES.UpdatesApprovedAndMissing = 0" of overall compliance state since it does not really work with the way we exclude deployments
60 | ---- Added option to exclude deployments with starttime in the future
61 | ---- Disabled update deployments will now be excluded from overall compliance state
62 | ---- 2021-06-14: Simplified RollupStatus and CurrentRollupStatus case when clauses
63 | ---- 2021-06-10: Added update install errors and changed query logic due to performance problems starting with 35k systems
64 | ---- 2020-11-30: Added "System Center Endpoint Protection" back to the exclusion list
65 | ---- 2020-11-24: Changed some descriptions, the QuickFixEngineering query due to missing date entries and the language problem from 20201103,
66 | ---- QuickFixEngineering query will also use the rollups as a reference if possible, simplified the current- and last-rollup queries and removed the limitation for Server 2008
67 | ---- 2020-11-03: Changed 'max(CASE WHEN (ISDATE(QFE.InstalledOn0) = 0' to 'max(CASE WHEN (LEN(QFE.InstalledOn0) > 10' due to language problems
68 | ---- 2020-11-03: Changed 'Windows Defender' to 'Microsoft Defender Antivirus'
69 |
70 | ----- START just for testing in SQL directly
71 | --Declare @CollectionID as varchar(max) = 'SMS00001' ---- semicolon seperated list of collectionIDs
72 | --Declare @ExcludeProductList as varchar(max) = N'Microsoft Defender Antivirus;System Center Endpoint Protection'; ---- semicolon seperated list of update products
73 | --Declare @MonthIndex as int = 0; -- 0 = current month, 1 = previous month
74 |
75 | ---- Using a bitmask like parameter. Makes it possible to use a multivalue parameter instead of multiple parameters to filter out some deployments
76 | ---- Using the same CTE function to convert parameter arraylist to CTE as with other multi value parameters due to perf issues on some SQL systems
77 | --Declare @ExcludeDeplBitMask as varchar(20) = '0'---'2;4;8;16';
78 | ---- 2 = Deployments with starttime in the future will be excluded
79 | ---- 4 = Deployments with deadline in the future will be excluded
80 | ---- 8 = Available deployments will be excluded
81 | ---- 16 = Disabled deployments will be excluded
82 | ----- END just for testing in SQL directly
83 |
84 | ---- Extra variables to prevent variable performance issues in some SQL configurations
85 | Declare @CollectionIDList as varchar(max) = @CollectionID ---- semicolon seperated list of collectionIDs
86 | Declare @ExcludeProducts as varchar(max) = @ExcludeProductList ---- semicolon seperated list of update products
87 | Declare @MonthIndexInternal as int = @MonthIndex
88 | Declare @ExcludeDeplBitMaskInternal as varchar(20) = @ExcludeDeplBitMask
89 |
90 | -- using prefix like "2017-07" to filter for the security rollup of the past month
91 | DECLARE @LastRollupPrefix as char(7);
92 | DECLARE @CurrentRollupPrefix as char(7);
93 | DECLARE @SecondTuesdayOfMonth datetime;
94 |
95 | -- Calculate 2nd Tuesday of month to add the correct date string in case install date is missing in v_GS_QUICK_FIX_ENGINEERING
96 | -- Also used to fill the gap between the first day of a month and the next time a rollup will be released
97 | SET @SecondTuesdayOfMonth = (DATEADD(Month, DATEDIFF(Month, 0, GETDATE()), 0) + 6 + 7 - (DATEPART(Weekday, DATEADD(Month, DATEDIFF(Month, 0, GETDATE()), 0)) + (@@DateFirst + 3) + 7) %7)
98 |
99 | IF GETDATE() < @SecondTuesdayOfMonth
100 | BEGIN
101 | SET @LastRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-2-@MonthIndexInternal,GETDATE()),126))
102 | SET @CurrentRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-1-@MonthIndexInternal,GETDATE()),126))
103 | END
104 | ELSE
105 | BEGIN
106 | SET @LastRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-1-@MonthIndexInternal,GETDATE()),126))
107 | SET @CurrentRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,0-@MonthIndexInternal,GETDATE()),126))
108 | END;
109 |
110 | -- PRE QUERIES
111 | -- Create table for collection IDs. Converting string based list into CTE to avoid any parameter performance issues with certain SQL configurations.
112 | WITH CTE_CollIDPieces
113 | AS
114 | (
115 | SELECT 1 AS ID
116 | ,1 AS [StartString]
117 | ,Cast(CHARINDEX(';', @CollectionIDList,0) as int) AS StopString
118 | UNION ALL
119 | SELECT ID + 1
120 | ,StopString + 1
121 | ,Cast(CHARINDEX(';', @CollectionIDList, StopString + 1) as int)
122 | FROM CTE_CollIDPieces
123 | WHERE StopString > 0
124 | )
125 | ,CTE_CollIDs
126 | AS
127 | (
128 | SELECT (SUBSTRING(@CollectionIDList, StartString,
129 | CASE WHEN StopString > 0 THEN StopString - StartString
130 | ELSE LEN(@CollectionIDList)
131 | END)) AS CollectionID
132 | FROM CTE_CollIDPieces
133 | )
134 | -- Create CTE for excluded products. Converting string based list into CTE to avoid any parameter performance issues with certain SQL configurations.
135 | ,CTE_ProductPieces
136 | AS
137 | (
138 | SELECT 1 AS ID
139 | ,1 AS [StartString]
140 | ,Cast(CHARINDEX(';', @ExcludeProducts,0) as int) AS StopString
141 | UNION ALL
142 | SELECT ID + 1
143 | ,StopString + 1
144 | ,Cast(CHARINDEX(';', @ExcludeProducts, StopString + 1) as int)
145 | FROM CTE_ProductPieces
146 | WHERE StopString > 0
147 | )
148 | ,CTE_Products
149 | AS
150 | (
151 | SELECT (SUBSTRING(@ExcludeProducts, StartString,
152 | CASE WHEN StopString > 0 THEN StopString - StartString
153 | ELSE LEN(@ExcludeProducts)
154 | END)) AS Product
155 | FROM CTE_ProductPieces
156 | ),
157 | -- Using the same CTE function to convert parameter arraylist to CTE as with other multi value parameters due to perf issues on some SQL systems
158 | CTE_ExcludePieces
159 | AS
160 | (
161 | SELECT 1 AS ID
162 | ,1 AS [StartString]
163 | ,Cast(CHARINDEX(';', @ExcludeDeplBitMaskInternal,0) as int) AS StopString
164 | UNION ALL
165 | SELECT ID + 1
166 | ,StopString + 1
167 | ,Cast(CHARINDEX(';', @ExcludeDeplBitMaskInternal, StopString + 1) as int)
168 | FROM CTE_ExcludePieces
169 | WHERE StopString > 0
170 | )
171 | ,CTE_ExcludeIDs
172 | AS
173 | (
174 | SELECT (SUBSTRING(@ExcludeDeplBitMaskInternal, StartString,
175 | CASE WHEN StopString > 0 THEN StopString - StartString
176 | ELSE LEN(@ExcludeDeplBitMaskInternal)
177 | END)) AS ExcludeID
178 | FROM CTE_ExcludePieces
179 | ),
180 | -- generate list of systems we are insterested in
181 | ResourceList (ResourceID) as
182 | (
183 | Select Distinct ResourceID from v_FullCollectionMembership FCM
184 | inner join CTE_CollIDs on CTE_CollIDs.CollectionID = FCM.CollectionID
185 | ),
186 | -- List of deployments we might need to exclude
187 | ExcludedDeploymentsBaseList (AssignmentID, AssignmentType) as
188 | (
189 | --- Getting a list of deployments/assignments based on different criteria to further limit the output later on
190 | -- parameter to be able to toggle to ex or include future deployments
191 | -- deployments with STARTTIME in the future
192 | select CIA.AssignmentID, AssignmentType = 2
193 | from v_CIAssignment cia
194 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
195 | and cia.StartTime > GETDATE()
196 | -- deployments with DEADLINE in the future
197 | UNION ALL
198 | select CIA.AssignmentID, AssignmentType = 4
199 | from v_CIAssignment cia
200 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
201 | and cia.EnforcementDeadline > GETDATE()
202 | -- availabe deployments
203 | UNION ALL
204 | select CIA.AssignmentID, AssignmentType = 8
205 | from v_CIAssignment cia
206 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
207 | and cia.EnforcementDeadline is null
208 | -- disabled deployments
209 | UNION ALL
210 | select CIA.AssignmentID, AssignmentType = 16
211 | from v_CIAssignment cia
212 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
213 | and cia.AssignmentEnabled = 0
214 | ),
215 | ExcludedDeployments (AssignmentID,AssignmentType, CI_ID) as
216 | (
217 | -- Select just the deployments we need to exclude
218 | Select AssignmentID, AssignmentType, CI_ID = 0 from ExcludedDeploymentsBaseList
219 | where AssignmentType in (Select ExcludeID from CTE_ExcludeIDs)
220 | UNION ALL
221 | -- Defender Updates will be filtered out, because they will be updated more frequently
222 | -- Other producst can be filtered as well via @ExcludeProductList
223 | select CIA.AssignmentID, AssignmentType = 1, CIATOCI.CI_ID
224 | from v_CIAssignment CIA
225 | inner join v_CIAssignmentToCI CIATOCI on CIATOCI.AssignmentID = CIA.AssignmentID
226 | inner join v_CICategoryInfo CICI on CICI.Ci_ID = CIATOCI.CI_ID
227 | inner join CTE_Products PR on PR.Product = CICI.CategoryInstanceName
228 | ),
229 | -- list of cumulative updates of the current and the past month
230 | CumulativeUpdates (ArticleID,CI_ID, Latest) as
231 | (
232 | Select 'KB' + updi.ArticleID as Article, CI_ID, Latest = 0 from v_updateinfo updi where (updi.Title like @LastRollupPrefix + ' Cumulative Update for Windows%' or updi.Title like @LastRollupPrefix + ' Security Monthly Quality Rollup%' or updi.Title like @LastRollupPrefix + ' Cumulative Update for Microsoft server operating system%')
233 | UNION ALL
234 | Select 'KB' + updi.ArticleID as Article, CI_ID, Latest = 1 from v_updateinfo updi where (updi.Title like @CurrentRollupPrefix + ' Cumulative Update for Windows%' or updi.Title like @CurrentRollupPrefix + ' Security Monthly Quality Rollup%' or updi.Title like @CurrentRollupPrefix + ' Cumulative Update for Microsoft server operating system%')
235 | )
236 |
237 |
238 | --MAIN QUERY
239 | select [Name] = VRS.Name0
240 | ,[ResourceID] = VRS.ResourceID
241 | ,[Counter] = 1 -- a counter to make it easier to count in reporting services
242 | ,[Domain] = VRS.Resource_Domain_OR_Workgr0
243 | ,[PendingReboot] = BGBL.ClientState
244 | ,[OSType] = GOS.Caption0
245 | ,[OSBuild] = BGBL.DeviceOSBuild
246 | ,[ClientVersion] = VRS.Client_Version0
247 | ,[WSUSVersion] = USS.LastWUAVersion
248 | ,[DefenderPattern] = AHS.AntivirusSignatureVersion
249 | ,[DefenderPatternAge] = AHS.AntivirusSignatureAge
250 | ,[WSUSScanError] = USS.LastErrorCode
251 | ,[DaysSinceLastOnline] = ISNULL((DateDiff(DAY,BGBL.LastPolicyRequest,GETDATE())),999)
252 | ,[DaysSinceLastAADSLogon] = ISNULL((DateDiff(DAY,VRS.Last_Logon_Timestamp0,GETDATE())), 999)
253 | ,[DaysSinceLastBoot] = ISNULL((DateDiff(DAY,GOS.LastBootUpTime0,GETDATE())), 999)
254 | ,[DaysSinceLastUpdateInstall] = ISNULL((DateDiff(DAY,UPDINSTDATE.LastInstallTime,GETDATE())), 999)
255 | ,[MonthSinceLastOnline] = ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999)
256 | ,[MonthSinceLastOnlineABC] = case when ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999) = 0 then 'A'
257 | when ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999) = 1 then 'B' else 'C' end
258 | ,[MonthSinceLastAADSLogon] = ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999)
259 | ,[MonthSinceLastAADSLogonABC] = case when ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999) = 0 then 'A'
260 | when ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999) = 1 then 'B' else 'C' end
261 | ,[MonthSinceLastBoot] = ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999)
262 | ,[MonthSinceLastBootABC] = case when ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999) = 0 then 'A'
263 | when ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999) = 1 then 'B' else 'C' end
264 | ,[MonthSinceLastUpdateInstall] = ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999)
265 | ,[MonthSinceLastUpdateInstallABC] = case when ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999) = 0 then 'A'
266 | when ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999) = 1 then 'B' else 'C' end
267 | ,[MonthSinceLastUpdateScan] = ISNULL((DateDiff(MONTH,USS.LastScanTime,GETDATE())), 999)
268 | ---- custom compliance state based on deploymentstatus, update status, last installdate and last rollup state. Last update install needs to be at least in the past month
269 | ---- 20240506: Changed compliance and removed last install date. Makes no sense when we also look for the last or current rollup
270 | ,[OverallComplianceState] = Case when AssignmentStatus.Compliant = AssignmentStatus.AssignmentSum and (LASTROLLUPSTAT.Status = 3 or CURRROLLUPSTAT.Status = 3) then 1 else 0 end
271 | ---- 20210801: Changed compliance to contain the last rollup
272 | ---- ,[OverallComplianceState] = Case when AssignmentStatus.Compliant = AssignmentStatus.AssignmentSum and (DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE()) <= 1+@MonthIndexInternal) and (LASTROLLUPSTAT.Status = 3 or CURRROLLUPSTAT.Status = 3) then 1 else 0 end
273 | ---- 20210801: Original UpdateAssignmentCompliance query without last rollup state and with UPDATESTATES.UpdatesApprovedAndMissing = 0
274 | ---- Query does not work anymore because of the way we filter future and not enabled update deployments and the changed name to "OverallComplianceState"
275 | --,[UpdateAssignmentCompliance] = Case when AssignmentStatus.Compliant = AssignmentStatus.AssignmentSum and UPDATESTATES.UpdatesApprovedAndMissing = 0 and (DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE()) <= 1) then 1 else 0 end
276 | ---- "UpdateStatusCompliant" is now called "UpdateAssignmentCompliant"
277 | ,[UpdateAssignmentCompliant] = AssignmentStatus.Compliant
278 | ,[UpdateAssignmentSum] = AssignmentStatus.AssignmentSum
279 | ,[UpdateAssignmentNonCompliant] = AssignmentStatus.AssignmentSum - AssignmentStatus.Compliant
280 | --,[UpdateStatusNonCompliant] = AssignmentStatus.NonCompliant
281 | --,[UpdateStatusUnknown] = case when AssignmentStatus.Unknown is null then 999
282 | --when (AssignmentStatus.Compliant is null and AssignmentStatus.NonCompliant is null and AssignmentStatus.Failed is null and AssignmentStatus.Pending is null) then 999
283 | --else AssignmentStatus.Unknown end ---some are unknown, because the state message seems to be missing
284 | --,[UpdateStatusFailed] = AssignmentStatus.Failed
285 | --,[UpdateStatusPending] = AssignmentStatus.Pending
286 | ,[MissingUpdatesAll] = UPDATESTATES.UpdatesMissingAll
287 | ,[MissingUpdatesApproved] = UPDATESTATES.UpdatesApprovedAndMissing
288 | ,[UpdatesApprovedAll] = UPDATESTATES.UpdatesApproved
289 | ,[UpdatesApprovedMissingAndError] = UPDATESTATES.UpdatesApprovedAndMissingAndError
290 | ---- show status of reference security rollup update
291 | ---- if current rollup ist installed, the last one doesn't matter and will be set to installed as well
292 | ,[LastRollupStatus] = case when LASTROLLUPSTAT.Status = 3 or CURRROLLUPSTAT.Status = 3 then 3 else 2 end
293 | ,[CurrentRollupStatus] = case when CURRROLLUPSTAT.Status = 3 then 3 else 2 end
294 | ---- add a list of all the collections having a software update or all deployments maintenance window
295 | --,[UpdateMWs] = STUFF((select ';' + Description
296 | -- from v_FullCollectionMembership TX0
297 | -- inner join v_ServiceWindow TX1 on TX1.CollectionID = TX0.CollectionID
298 | -- where TX1.ServiceWindowType in (1,4) and TX0.ResourceID = VRS.ResourceID FOR XML PATH('')), 1, 1, '')
299 | ---- add a list of all collections with update deployments to see gaps
300 | ,[UpdateCollections] = STUFF((select '; ' + CIA.CollectionName
301 | from v_FullCollectionMembership FCM
302 | inner join v_CIAssignment CIA on CIA.CollectionID = FCM.CollectionID
303 | where CIA.AssignmentType in (1,5) and FCM.ResourceID = VRS.ResourceID
304 | group by FCM.ResourceID, CIA.CollectionName FOR XML PATH('')), 1, 1, '')
305 | from v_R_System VRS
306 | inner join ResourceList on ResourceList.ResourceID = VRS.ResourceID
307 | ----- Join update states
308 | left join (
309 | Select ucs.ResourceID
310 | ,[UpdatesApproved] = sum(case when AssignedUpdates.CI_ID is not null then 1 else 0 end)
311 | ,[UpdatesApprovedAndMissing] = sum(case when AssignedUpdates.CI_ID is not null and ucs.Status = 2 then 1 else 0 end)
312 | ,[UpdatesMissingAll] = sum(case when ucs.Status = 2 then 1 else 0 end)
313 | ,[UpdatesApprovedAndMissingAndError] = sum(case when AssignedUpdates.CI_ID is not null and ucs.Status = 2 and (ucs.LastErrorCode is not null and ucs.LastErrorCode !=0) then 1 else 0 end)
314 | from v_UpdateComplianceStatus ucs
315 | inner join v_UpdateCIs upd on upd.CI_ID = ucs.CI_ID
316 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
317 | left join (
318 | -- updates deployed to the system
319 | select ATM.ResourceID, CATC.CI_ID
320 | from v_CIAssignmentTargetedMachines ATM
321 | inner join v_CIAssignmentToCI CATC on CATC.AssignmentID = ATM.AssignmentID
322 | inner join ResourceList on ResourceList.ResourceID = ATM.ResourceID
323 | inner join v_CIAssignment CIA on CIA.AssignmentID = CATC.AssignmentID
324 | where CIA.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
325 | and CIA.AssignmentID not in (Select AssignmentID from ExcludedDeployments) -- Exclude some deployments based on parameter settings
326 | group by ATM.ResourceID, CATC.CI_ID
327 | ) as AssignedUpdates on UCS.ResourceID = AssignedUpdates.ResourceID and AssignedUpdates.CI_ID = UCS.CI_ID
328 | where upd.IsHidden = 0 and upd.CIType_ID in (1,8) --- 1 = update, 8 = update bundle, 9 = update group
329 | and ucs.CI_ID not in (Select CI_ID from ExcludedDeployments) -- exclude Defender and SCEP from statistic
330 | group by ucs.ResourceID
331 | ) UPDATESTATES on UPDATESTATES.ResourceID = VRS.ResourceID
332 | --- join last Rollup as reference
333 | left join (
334 | select UCS.ResourceID,
335 | max(UCS.Status) as Status
336 | from v_UpdateComplianceStatus UCS
337 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
338 | inner join CumulativeUpdates CUU on CUU.CI_ID = UCS.CI_ID and CUU.Latest = 0
339 | group by UCS.ResourceID
340 | ) as LASTROLLUPSTAT on LASTROLLUPSTAT.ResourceID = VRS.ResourceID
341 | --- join current Rollup as reference
342 | left join (
343 | select UCS.ResourceID,
344 | max(UCS.Status) as Status
345 | from v_UpdateComplianceStatus UCS
346 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
347 | inner join CumulativeUpdates CUU on CUU.CI_ID = UCS.CI_ID and CUU.Latest = 1
348 | group by UCS.ResourceID
349 | ) as CURRROLLUPSTAT on CURRROLLUPSTAT.ResourceID = VRS.ResourceID
350 | --- Join OS Information
351 | left join v_GS_OPERATING_SYSTEM GOS on GOS.ResourceID = VRS.ResourceID
352 | --- Join Client Health status
353 | --left join v_CH_ClientSummary CHCS on CHCS.ResourceID = VRS.ResourceID
354 | --- Join WSUS Client Info
355 | left join v_UpdateScanStatus USS on USS.ResourceID = VRS.ResourceID
356 | --- Join Antimalware Info
357 | left join v_GS_AntimalwareHealthStatus AHS on AHS.ResourceID = VRS.ResourceID
358 | --- join update compliance status (for overall compliance, failed and pending status)
359 | left join (
360 | SELECT uas.ResourceID,
361 | count(uas.ResourceID) as AssignmentSum,
362 | --max(uas.LastComplianceMessageTime) as LastComplianceMessageTime,
363 | sum(CASE WHEN (IsCompliant = 1) THEN 1 ELSE 0 END) AS Compliant
364 | --sum(CASE WHEN (IsCompliant = 0) THEN 1 ELSE 0 END) AS NonCompliant,
365 | --sum(CASE WHEN (IsCompliant is null) THEN 1 ELSE 0 END) AS Unknown,
366 | --sum(CASE WHEN (IsCompliant = 0) AND LastEnforcementMessageID in (6,9) THEN 1 ELSE 0 END) AS Failed,
367 | --sum(CASE WHEN (IsCompliant = 0) AND LastEnforcementMessageID not in (0,6,9) THEN 1 ELSE 0 END) AS Pending
368 | FROM v_UpdateAssignmentStatus uas
369 | inner join ResourceList on ResourceList.ResourceID = uas.ResourceID
370 | where UAS.AssignmentID not in (Select AssignmentID from ExcludedDeployments) -- exclude defender and scep deployments from compliants as well as other deployments if selected
371 | group by uas.ResourceID
372 | ) as AssignmentStatus on AssignmentStatus.ResourceID = VRS.ResourceID
373 | ---- join last update installdate for security updates just as a reference to find problematic clients not installing security updates at all
374 | ---- need to convert datetime for older OS from 64bit binary to datetime
375 | left join (
376 | select qfe.ResourceID
377 | --- CUU.CI_ID is not null means the current rollup is installed and we can use that installdate as reference
378 | ,LastInstallTime = max(CASE WHEN CUU.CI_ID is not null then
379 | (
380 | --- the current rollup is installed, but do we have a valid install date? In some cases the installdate seems to be mising. For some Win10 1803 systems for example.
381 | --- "-05" impossible date, since the update could not be released the 5th day of the month (normally). That should indicate the missing date info and give us enough info about the state of the system
382 | --CASE WHEN qfe.InstalledOn0 = '' THEN @UpdatePrefix + '-05 00:00:00' ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101) END
383 | CASE WHEN qfe.InstalledOn0 = '' THEN @CurrentRollupPrefix + '-05 00:00:00' ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101) END
384 | )
385 | else --CUU.CI_ID IS null
386 | (
387 | --- due to some older systems sending datetime as binary like this: 01cc31160e1c4bac and since there is no cumulative update for such systems "CUU.CI_ID is null" should always be valid
388 | --- Found three different date strings so far: MM/dd/yyyy or yyyMMdd or binary
389 | CASE WHEN (LEN(QFE.InstalledOn0) > 10)
390 | THEN CAST((TRY_CONVERT(BIGINT,TRY_CONVERT(VARBINARY(64), '0x' + QFE.InstalledOn0,1)) / 864000000000.0 - 109207) AS DATETIME)
391 | ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101)
392 | END
393 | ) END)
394 | from v_GS_QUICK_FIX_ENGINEERING QFE
395 | inner join ResourceList on ResourceList.ResourceID = QFE.ResourceID
396 | left join CumulativeUpdates CUU on CUU.ArticleID = QFE.HotFixID0
397 | --where (QFE.Description0 = 'Security Update' or QFE.Description0 is null)
398 | group by qfe.ResourceID
399 | ) UPDINSTDATE on UPDINSTDATE.ResourceID = VRS.ResourceID
400 | ---- join Pending Reboot Info
401 | --left join BGB_LiveData BGBL on BGBL.ResourceID = VRS.ResourceID ---- <- no direct rights assigned and no other view available, using v_CombinedDeviceResources instead
402 | left join v_CombinedDeviceResources BGBL on BGBL.MachineID = VRS.ResourceID
403 |
404 | ---- fix for SQL compat level problem
405 | ---- https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
406 | ---- Force legacy cardinality for SQL server versions before 2016 SP1 (SQL version less than 13.0.4001.0)
407 | --OPTION (QUERYTRACEON 9481)
408 | ---- Force legacy cardinality for SQL server versions 2016 SP1 and higher (SQL version equal or greater than 13.0.4001.0)
409 | --OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))
410 | true
411 |
412 |
413 |
414 | Name
415 | System.String
416 |
417 |
418 | ResourceID
419 | System.Int32
420 |
421 |
422 | Counter
423 | System.Int32
424 |
425 |
426 | Domain
427 | System.String
428 |
429 |
430 | PendingReboot
431 | System.Int32
432 |
433 |
434 | OSType
435 | System.String
436 |
437 |
438 | OSBuild
439 | System.String
440 |
441 |
442 | ClientVersion
443 | System.String
444 |
445 |
446 | WSUSVersion
447 | System.String
448 |
449 |
450 | DefenderPattern
451 | System.String
452 |
453 |
454 | DaysSinceLastOnline
455 | System.Int32
456 |
457 |
458 | DefenderPatternAge
459 | System.Int32
460 |
461 |
462 | WSUSScanError
463 | System.Int32
464 |
465 |
466 | DaysSinceLastAADSLogon
467 | System.Int32
468 |
469 |
470 | DaysSinceLastBoot
471 | System.Int32
472 |
473 |
474 | DaysSinceLastUpdateInstall
475 | System.Int32
476 |
477 |
478 | MonthSinceLastOnlineABC
479 | System.String
480 |
481 |
482 | MonthSinceLastOnline
483 | System.Int32
484 |
485 |
486 | MonthSinceLastAADSLogon
487 | System.Int32
488 |
489 |
490 | MonthSinceLastAADSLogonABC
491 | System.String
492 |
493 |
494 | MonthSinceLastBoot
495 | System.Int32
496 |
497 |
498 | MonthSinceLastBootABC
499 | System.String
500 |
501 |
502 | MonthSinceLastUpdateInstall
503 | System.Int32
504 |
505 |
506 | MonthSinceLastUpdateInstallABC
507 | System.String
508 |
509 |
510 | MonthSinceLastUpdateScan
511 | System.Int32
512 |
513 |
514 | OverallComplianceState
515 | System.Int32
516 |
517 |
518 | UpdateAssignmentCompliant
519 | System.Int32
520 |
521 |
522 | UpdateAssignmentSum
523 | System.Int32
524 |
525 |
526 | UpdateAssignmentNonCompliant
527 | System.Int32
528 |
529 |
530 | MissingUpdatesAll
531 | System.Int32
532 |
533 |
534 | MissingUpdatesApproved
535 | System.Int32
536 |
537 |
538 | UpdatesApprovedAll
539 | System.Int32
540 |
541 |
542 | UpdatesApprovedMissingAndError
543 | System.Int32
544 |
545 |
546 | LastRollupStatus
547 | System.Int32
548 |
549 |
550 | CurrentRollupStatus
551 | System.Int32
552 |
553 |
554 | UpdateCollections
555 | System.String
556 |
557 |
558 |
559 | http://reportserver.domain.local/ReportServer
560 |
--------------------------------------------------------------------------------
/SourceFiles/UpdatesSummary.sql:
--------------------------------------------------------------------------------
1 | ---SCRIPTVERSION: 20220404
2 |
3 | -----------------------------------------------------------------------------------------------------------------------
4 | ---- Disclaimer
5 | ----
6 | ---- This sample script is not supported under any Microsoft standard support program or service. This sample
7 | ---- script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties
8 | ---- including, without limitation, any implied warranties of merchantability or of fitness for a particular
9 | ---- purpose. The entire risk arising out of the use or performance of this sample script and documentation
10 | ---- remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation,
11 | ---- production, or delivery of this script be liable for any damages whatsoever (including, without limitation,
12 | ---- damages for loss of business profits, business interruption, loss of business information, or other
13 | ---- pecuniary loss) arising out of the use of or inability to use this sample script or documentation, even
14 | ---- if Microsoft has been advised of the possibility of such damages.
15 | -----------------------------------------------------------------------------------------------------------------------
16 | ---- Changelog:
17 | ---- 2022-04-04: Changed the way deployed updates are shown. "MissingUpdatesApproved" will not show updates for excluded deployments anymore. Makes it more consistant with the rest of the report
18 | ---- Added systems domain name to the list
19 | ---- Removed unnecessary where clause: "where (QFE.Description0 = 'Security Update' or QFE.Description0 is null)"
20 | ---- Added Parameter @MonthIndex to be able to set the report one month back. Only possible if updates are delayed for one month and not deployed within the month of their release date
21 | ---- Added Paremeter to be able exclude future deployments based on starttime or deadline, exclude available and diabled deployments
22 | ---- 2022-03-02: Fixed @SecondTuesdayOfMonth and change logic for pulling the current and last rollups. Based on pull request 11
23 | ---- 2021-11-18: Added "Cumulative Update for Microsoft server operating system" string for server 2022 updates.
24 | ---- 2021-08-01: Changed multi value parameter handling to use CTEs due to performance issues with some environments.
25 | ---- Also changed overall compliance to be only compliant in case the last rollup has been installed and changed name from "UpdateAssignmentCompliance" to "OverallComplianceState"
26 | ---- Changed name from "UpdateStatusCompliant" to "UpdateAssignmentCompliant" to be more accurate with the wording
27 | ---- Removed condition "UPDATESTATES.UpdatesApprovedAndMissing = 0" of overall compliance state since it does not really work with the way we exclude deployments
28 | ---- Added option to exclude deployments with starttime in the future
29 | ---- Disabled update deployments will now be excluded from overall compliance state
30 | ---- 2021-06-14: Simplified RollupStatus and CurrentRollupStatus case when clauses
31 | ---- 2021-06-10: Added update install errors and changed query logic due to performance problems starting with 35k systems
32 | ---- 2020-11-30: Added "System Center Endpoint Protection" back to the exclusion list
33 | ---- 2020-11-24: Changed some descriptions, the QuickFixEngineering query due to missing date entries and the language problem from 20201103,
34 | ---- QuickFixEngineering query will also use the rollups as a reference if possible, simplified the current- and last-rollup queries and removed the limitation for Server 2008
35 | ---- 2020-11-03: Changed 'max(CASE WHEN (ISDATE(QFE.InstalledOn0) = 0' to 'max(CASE WHEN (LEN(QFE.InstalledOn0) > 10' due to language problems
36 | ---- 2020-11-03: Changed 'Windows Defender' to 'Microsoft Defender Antivirus'
37 |
38 | --- START just for testing in SQL directly
39 | Declare @CollectionID as varchar(max) = 'SMS00001' ---- semicolon seperated list of collectionIDs
40 | Declare @ExcludeProductList as varchar(max) = N'Microsoft Defender Antivirus;System Center Endpoint Protection'; ---- semicolon seperated list of update products
41 | Declare @MonthIndex as int = 0; -- 0 = current month, 1 = previous month
42 |
43 | -- Using a bitmask like parameter. Makes it possible to use a multivalue parameter instead of multiple parameters to filter out some deployments
44 | -- Using the same CTE function to convert parameter arraylist to CTE as with other multi value parameters due to perf issues on some SQL systems
45 | Declare @ExcludeDeplBitMask as varchar(20) = '8;16'---'2;4;8;16';
46 | -- 2 = Deployments with starttime in the future will be excluded
47 | -- 4 = Deployments with deadline in the future will be excluded
48 | -- 8 = Available deployments will be excluded
49 | -- 16 = Disabled deployments will be excluded
50 | --- END just for testing in SQL directly
51 |
52 | ---- Extra variables to prevent variable performance issues in some SQL configurations
53 | Declare @CollectionIDList as varchar(max) = @CollectionID ---- semicolon seperated list of collectionIDs
54 | Declare @ExcludeProducts as varchar(max) = @ExcludeProductList ---- semicolon seperated list of update products
55 | Declare @MonthIndexInternal as int = @MonthIndex
56 | Declare @ExcludeDeplBitMaskInternal as varchar(20) = @ExcludeDeplBitMask
57 |
58 | -- using prefix like "2017-07" to filter for the security rollup of the past month
59 | DECLARE @LastRollupPrefix as char(7);
60 | DECLARE @CurrentRollupPrefix as char(7);
61 | DECLARE @SecondTuesdayOfMonth datetime;
62 |
63 | -- Calculate 2nd Tuesday of month to add the correct date string in case install date is missing in v_GS_QUICK_FIX_ENGINEERING
64 | -- Also used to fill the gap between the first day of a month and the next time a rollup will be released
65 | SET @SecondTuesdayOfMonth = (DATEADD(Month, DATEDIFF(Month, 0, GETDATE()), 0) + 6 + 7 - (DATEPART(Weekday, DATEADD(Month, DATEDIFF(Month, 0, GETDATE()), 0)) + (@@DateFirst + 3) + 7) %7)
66 |
67 | IF GETDATE() < @SecondTuesdayOfMonth
68 | BEGIN
69 | SET @LastRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-2-@MonthIndexInternal,GETDATE()),126))
70 | SET @CurrentRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-1-@MonthIndexInternal,GETDATE()),126))
71 | END
72 | ELSE
73 | BEGIN
74 | SET @LastRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,-1-@MonthIndexInternal,GETDATE()),126))
75 | SET @CurrentRollupPrefix = (SELECT convert(char(7),DATEADD(MONTH,0-@MonthIndexInternal,GETDATE()),126))
76 | END;
77 |
78 | -- PRE QUERIES
79 | -- Create table for collection IDs. Converting string based list into CTE to avoid any parameter performance issues with certain SQL configurations.
80 | WITH CTE_CollIDPieces
81 | AS
82 | (
83 | SELECT 1 AS ID
84 | ,1 AS [StartString]
85 | ,Cast(CHARINDEX(';', @CollectionIDList,0) as int) AS StopString
86 | UNION ALL
87 | SELECT ID + 1
88 | ,StopString + 1
89 | ,Cast(CHARINDEX(';', @CollectionIDList, StopString + 1) as int)
90 | FROM CTE_CollIDPieces
91 | WHERE StopString > 0
92 | )
93 | ,CTE_CollIDs
94 | AS
95 | (
96 | SELECT (SUBSTRING(@CollectionIDList, StartString,
97 | CASE WHEN StopString > 0 THEN StopString - StartString
98 | ELSE LEN(@CollectionIDList)
99 | END)) AS CollectionID
100 | FROM CTE_CollIDPieces
101 | )
102 | -- Create CTE for excluded products. Converting string based list into CTE to avoid any parameter performance issues with certain SQL configurations.
103 | ,CTE_ProductPieces
104 | AS
105 | (
106 | SELECT 1 AS ID
107 | ,1 AS [StartString]
108 | ,Cast(CHARINDEX(';', @ExcludeProducts,0) as int) AS StopString
109 | UNION ALL
110 | SELECT ID + 1
111 | ,StopString + 1
112 | ,Cast(CHARINDEX(';', @ExcludeProducts, StopString + 1) as int)
113 | FROM CTE_ProductPieces
114 | WHERE StopString > 0
115 | )
116 | ,CTE_Products
117 | AS
118 | (
119 | SELECT (SUBSTRING(@ExcludeProducts, StartString,
120 | CASE WHEN StopString > 0 THEN StopString - StartString
121 | ELSE LEN(@ExcludeProducts)
122 | END)) AS Product
123 | FROM CTE_ProductPieces
124 | ),
125 | -- Using the same CTE function to convert parameter arraylist to CTE as with other multi value parameters due to perf issues on some SQL systems
126 | CTE_ExcludePieces
127 | AS
128 | (
129 | SELECT 1 AS ID
130 | ,1 AS [StartString]
131 | ,Cast(CHARINDEX(';', @ExcludeDeplBitMaskInternal,0) as int) AS StopString
132 | UNION ALL
133 | SELECT ID + 1
134 | ,StopString + 1
135 | ,Cast(CHARINDEX(';', @ExcludeDeplBitMaskInternal, StopString + 1) as int)
136 | FROM CTE_ExcludePieces
137 | WHERE StopString > 0
138 | )
139 | ,CTE_ExcludeIDs
140 | AS
141 | (
142 | SELECT (SUBSTRING(@ExcludeDeplBitMaskInternal, StartString,
143 | CASE WHEN StopString > 0 THEN StopString - StartString
144 | ELSE LEN(@ExcludeDeplBitMaskInternal)
145 | END)) AS ExcludeID
146 | FROM CTE_ExcludePieces
147 | ),
148 | -- generate list of systems we are insterested in
149 | ResourceList (ResourceID) as
150 | (
151 | Select Distinct ResourceID from v_FullCollectionMembership FCM
152 | inner join CTE_CollIDs on CTE_CollIDs.CollectionID = FCM.CollectionID
153 | ),
154 | -- List of deployments we might need to exclude
155 | ExcludedDeploymentsBaseList (AssignmentID, AssignmentType) as
156 | (
157 | --- Getting a list of deployments/assignments based on different criteria to further limit the output later on
158 | -- parameter to be able to toggle to ex or include future deployments
159 | -- deployments with STARTTIME in the future
160 | select CIA.AssignmentID, AssignmentType = 2
161 | from v_CIAssignment cia
162 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
163 | and cia.StartTime > GETDATE()
164 | -- deployments with DEADLINE in the future
165 | UNION ALL
166 | select CIA.AssignmentID, AssignmentType = 4
167 | from v_CIAssignment cia
168 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
169 | and cia.EnforcementDeadline > GETDATE()
170 | -- availabe deployments
171 | UNION ALL
172 | select CIA.AssignmentID, AssignmentType = 8
173 | from v_CIAssignment cia
174 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
175 | and cia.EnforcementDeadline is null
176 | -- disabled deployments
177 | UNION ALL
178 | select CIA.AssignmentID, AssignmentType = 16
179 | from v_CIAssignment cia
180 | where cia.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
181 | and cia.AssignmentEnabled = 0
182 | ),
183 | ExcludedDeployments (AssignmentID,AssignmentType, CI_ID) as
184 | (
185 | -- Select just the deployments we need to exclude
186 | Select AssignmentID, AssignmentType, CI_ID = 0 from ExcludedDeploymentsBaseList
187 | where AssignmentType in (Select ExcludeID from CTE_ExcludeIDs)
188 | UNION ALL
189 | -- Defender Updates will be filtered out, because they will be updated more frequently
190 | -- Other producst can be filtered as well via @ExcludeProductList
191 | select CIA.AssignmentID, AssignmentType = 1, CIATOCI.CI_ID
192 | from v_CIAssignment CIA
193 | inner join v_CIAssignmentToCI CIATOCI on CIATOCI.AssignmentID = CIA.AssignmentID
194 | inner join v_CICategoryInfo CICI on CICI.Ci_ID = CIATOCI.CI_ID
195 | inner join CTE_Products PR on PR.Product = CICI.CategoryInstanceName
196 | ),
197 | -- list of cumulative updates of the current and the past month
198 | CumulativeUpdates (ArticleID,CI_ID, Latest) as
199 | (
200 | Select 'KB' + updi.ArticleID as Article, CI_ID, Latest = 0 from v_updateinfo updi where (updi.Title like @LastRollupPrefix + ' Cumulative Update for Windows%' or updi.Title like @LastRollupPrefix + ' Security Monthly Quality Rollup%' or updi.Title like @LastRollupPrefix + ' Cumulative Update for Microsoft server operating system%')
201 | UNION ALL
202 | Select 'KB' + updi.ArticleID as Article, CI_ID, Latest = 1 from v_updateinfo updi where (updi.Title like @CurrentRollupPrefix + ' Cumulative Update for Windows%' or updi.Title like @CurrentRollupPrefix + ' Security Monthly Quality Rollup%' or updi.Title like @CurrentRollupPrefix + ' Cumulative Update for Microsoft server operating system%')
203 | )
204 |
205 |
206 | --MAIN QUERY
207 | select [Name] = VRS.Name0
208 | ,[ResourceID] = VRS.ResourceID
209 | ,[Counter] = 1 -- a counter to make it easier to count in reporting services
210 | ,[Domain] = VRS.Resource_Domain_OR_Workgr0
211 | ,[PendingReboot] = BGBL.ClientState
212 | ,[OSType] = GOS.Caption0
213 | ,[OSBuild] = BGBL.DeviceOSBuild
214 | ,[ClientVersion] = VRS.Client_Version0
215 | ,[WSUSVersion] = USS.LastWUAVersion
216 | ,[DefenderPattern] = AHS.AntivirusSignatureVersion
217 | ,[DefenderPatternAge] = AHS.AntivirusSignatureAge
218 | ,[WSUSScanError] = USS.LastErrorCode
219 | ,[DaysSinceLastOnline] = ISNULL((DateDiff(DAY,BGBL.LastPolicyRequest,GETDATE())),999)
220 | ,[DaysSinceLastAADSLogon] = ISNULL((DateDiff(DAY,VRS.Last_Logon_Timestamp0,GETDATE())), 999)
221 | ,[DaysSinceLastBoot] = ISNULL((DateDiff(DAY,GOS.LastBootUpTime0,GETDATE())), 999)
222 | ,[DaysSinceLastUpdateInstall] = ISNULL((DateDiff(DAY,UPDINSTDATE.LastInstallTime,GETDATE())), 999)
223 | ,[MonthSinceLastOnline] = ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999)
224 | ,[MonthSinceLastOnlineABC] = case when ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999) = 0 then 'A'
225 | when ISNULL((DateDiff(MONTH,BGBL.LastPolicyRequest,GETDATE())), 999) = 1 then 'B' else 'C' end
226 | ,[MonthSinceLastAADSLogon] = ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999)
227 | ,[MonthSinceLastAADSLogonABC] = case when ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999) = 0 then 'A'
228 | when ISNULL((DateDiff(MONTH,VRS.Last_Logon_Timestamp0,GETDATE())), 999) = 1 then 'B' else 'C' end
229 | ,[MonthSinceLastBoot] = ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999)
230 | ,[MonthSinceLastBootABC] = case when ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999) = 0 then 'A'
231 | when ISNULL((DateDiff(MONTH,GOS.LastBootUpTime0,GETDATE())), 999) = 1 then 'B' else 'C' end
232 | ,[MonthSinceLastUpdateInstall] = ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999)
233 | ,[MonthSinceLastUpdateInstallABC] = case when ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999) = 0 then 'A'
234 | when ISNULL((DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE())), 999) = 1 then 'B' else 'C' end
235 | ,[MonthSinceLastUpdateScan] = ISNULL((DateDiff(MONTH,USS.LastScanTime,GETDATE())), 999)
236 | ---- custom compliance state based on deploymentstatus, update status, last installdate and last rollup state. Last update install needs to be at least in the past month
237 | ---- 20210801: Changed compliance to contain the last rollup
238 | ,[OverallComplianceState] = Case when AssignmentStatus.Compliant = AssignmentStatus.AssignmentSum and (DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE()) <= 1+@MonthIndexInternal) and (LASTROLLUPSTAT.Status = 3 or CURRROLLUPSTAT.Status = 3) then 1 else 0 end
239 | ---- 20210801: Original UpdateAssignmentCompliance query without last rollup state and with UPDATESTATES.UpdatesApprovedAndMissing = 0
240 | ---- Query does not work anymore because of the way we filter future and not enabled update deployments and the changed name to "OverallComplianceState"
241 | --,[UpdateAssignmentCompliance] = Case when AssignmentStatus.Compliant = AssignmentStatus.AssignmentSum and UPDATESTATES.UpdatesApprovedAndMissing = 0 and (DateDiff(MONTH,UPDINSTDATE.LastInstallTime,GETDATE()) <= 1) then 1 else 0 end
242 | ---- "UpdateStatusCompliant" is now called "UpdateAssignmentCompliant"
243 | ,[UpdateAssignmentCompliant] = AssignmentStatus.Compliant
244 | ,[UpdateAssignmentSum] = AssignmentStatus.AssignmentSum
245 | ,[UpdateAssignmentNonCompliant] = AssignmentStatus.AssignmentSum - AssignmentStatus.Compliant
246 | --,[UpdateStatusNonCompliant] = AssignmentStatus.NonCompliant
247 | --,[UpdateStatusUnknown] = case when AssignmentStatus.Unknown is null then 999
248 | --when (AssignmentStatus.Compliant is null and AssignmentStatus.NonCompliant is null and AssignmentStatus.Failed is null and AssignmentStatus.Pending is null) then 999
249 | --else AssignmentStatus.Unknown end ---some are unknown, because the state message seems to be missing
250 | --,[UpdateStatusFailed] = AssignmentStatus.Failed
251 | --,[UpdateStatusPending] = AssignmentStatus.Pending
252 | ,[MissingUpdatesAll] = UPDATESTATES.UpdatesMissingAll
253 | ,[MissingUpdatesApproved] = UPDATESTATES.UpdatesApprovedAndMissing
254 | ,[UpdatesApprovedAll] = UPDATESTATES.UpdatesApproved
255 | ,[UpdatesApprovedMissingAndError] = UPDATESTATES.UpdatesApprovedAndMissingAndError
256 | ---- show status of reference security rollup update
257 | ---- if current rollup ist installed, the last one doesn't matter and will be set to installed as well
258 | ,[LastRollupStatus] = case when LASTROLLUPSTAT.Status = 3 or CURRROLLUPSTAT.Status = 3 then 3 else 2 end
259 | ,[CurrentRollupStatus] = case when CURRROLLUPSTAT.Status = 3 then 3 else 2 end
260 | ---- add a list of all the collections having a software update or all deployments maintenance window
261 | --,[UpdateMWs] = STUFF((select ';' + Description
262 | -- from v_FullCollectionMembership TX0
263 | -- inner join v_ServiceWindow TX1 on TX1.CollectionID = TX0.CollectionID
264 | -- where TX1.ServiceWindowType in (1,4) and TX0.ResourceID = VRS.ResourceID FOR XML PATH('')), 1, 1, '')
265 | ---- add a list of all collections with update deployments to see gaps
266 | ,[UpdateCollections] = STUFF((select '; ' + CIA.CollectionName
267 | from v_FullCollectionMembership FCM
268 | inner join v_CIAssignment CIA on CIA.CollectionID = FCM.CollectionID
269 | where CIA.AssignmentType in (1,5) and FCM.ResourceID = VRS.ResourceID
270 | group by FCM.ResourceID, CIA.CollectionName FOR XML PATH('')), 1, 1, '')
271 | from v_R_System VRS
272 | inner join ResourceList on ResourceList.ResourceID = VRS.ResourceID
273 | ----- Join update states
274 | left join (
275 | Select ucs.ResourceID
276 | ,[UpdatesApproved] = sum(case when AssignedUpdates.CI_ID is not null then 1 else 0 end)
277 | ,[UpdatesApprovedAndMissing] = sum(case when AssignedUpdates.CI_ID is not null and ucs.Status = 2 then 1 else 0 end)
278 | ,[UpdatesMissingAll] = sum(case when ucs.Status = 2 then 1 else 0 end)
279 | ,[UpdatesApprovedAndMissingAndError] = sum(case when AssignedUpdates.CI_ID is not null and ucs.Status = 2 and (ucs.LastErrorCode is not null and ucs.LastErrorCode !=0) then 1 else 0 end)
280 | from v_UpdateComplianceStatus ucs
281 | inner join v_UpdateCIs upd on upd.CI_ID = ucs.CI_ID
282 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
283 | left join (
284 | -- updates deployed to the system
285 | select ATM.ResourceID, CATC.CI_ID
286 | from v_CIAssignmentTargetedMachines ATM
287 | inner join v_CIAssignmentToCI CATC on CATC.AssignmentID = ATM.AssignmentID
288 | inner join ResourceList on ResourceList.ResourceID = ATM.ResourceID
289 | inner join v_CIAssignment CIA on CIA.AssignmentID = CATC.AssignmentID
290 | where CIA.AssignmentType in (1,5) -- 1 = updates, 5 = update groups
291 | and CIA.AssignmentID not in (Select AssignmentID from ExcludedDeployments) -- Exclude some deployments based on parameter settings
292 | group by ATM.ResourceID, CATC.CI_ID
293 | ) as AssignedUpdates on UCS.ResourceID = AssignedUpdates.ResourceID and AssignedUpdates.CI_ID = UCS.CI_ID
294 | where upd.IsHidden = 0 and upd.CIType_ID in (1,8) --- 1 = update, 8 = update bundle, 9 = update group
295 | and ucs.CI_ID not in (Select CI_ID from ExcludedDeployments) -- exclude Defender and SCEP from statistic
296 | group by ucs.ResourceID
297 | ) UPDATESTATES on UPDATESTATES.ResourceID = VRS.ResourceID
298 | --- join last Rollup as reference
299 | left join (
300 | select UCS.ResourceID,
301 | max(UCS.Status) as Status
302 | from v_UpdateComplianceStatus UCS
303 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
304 | inner join CumulativeUpdates CUU on CUU.CI_ID = UCS.CI_ID and CUU.Latest = 0
305 | group by UCS.ResourceID
306 | ) as LASTROLLUPSTAT on LASTROLLUPSTAT.ResourceID = VRS.ResourceID
307 | --- join current Rollup as reference
308 | left join (
309 | select UCS.ResourceID,
310 | max(UCS.Status) as Status
311 | from v_UpdateComplianceStatus UCS
312 | inner join ResourceList on ResourceList.ResourceID = ucs.ResourceID
313 | inner join CumulativeUpdates CUU on CUU.CI_ID = UCS.CI_ID and CUU.Latest = 1
314 | group by UCS.ResourceID
315 | ) as CURRROLLUPSTAT on CURRROLLUPSTAT.ResourceID = VRS.ResourceID
316 | --- Join OS Information
317 | left join v_GS_OPERATING_SYSTEM GOS on GOS.ResourceID = VRS.ResourceID
318 | --- Join Client Health status
319 | --left join v_CH_ClientSummary CHCS on CHCS.ResourceID = VRS.ResourceID
320 | --- Join WSUS Client Info
321 | left join v_UpdateScanStatus USS on USS.ResourceID = VRS.ResourceID
322 | --- Join Antimalware Info
323 | left join v_GS_AntimalwareHealthStatus AHS on AHS.ResourceID = VRS.ResourceID
324 | --- join update compliance status (for overall compliance, failed and pending status)
325 | left join (
326 | SELECT uas.ResourceID,
327 | count(uas.ResourceID) as AssignmentSum,
328 | --max(uas.LastComplianceMessageTime) as LastComplianceMessageTime,
329 | sum(CASE WHEN (IsCompliant = 1) THEN 1 ELSE 0 END) AS Compliant
330 | --sum(CASE WHEN (IsCompliant = 0) THEN 1 ELSE 0 END) AS NonCompliant,
331 | --sum(CASE WHEN (IsCompliant is null) THEN 1 ELSE 0 END) AS Unknown,
332 | --sum(CASE WHEN (IsCompliant = 0) AND LastEnforcementMessageID in (6,9) THEN 1 ELSE 0 END) AS Failed,
333 | --sum(CASE WHEN (IsCompliant = 0) AND LastEnforcementMessageID not in (0,6,9) THEN 1 ELSE 0 END) AS Pending
334 | FROM v_UpdateAssignmentStatus uas
335 | inner join ResourceList on ResourceList.ResourceID = uas.ResourceID
336 | where UAS.AssignmentID not in (Select AssignmentID from ExcludedDeployments) -- exclude defender and scep deployments from compliants as well as other deployments if selected
337 | group by uas.ResourceID
338 | ) as AssignmentStatus on AssignmentStatus.ResourceID = VRS.ResourceID
339 | ---- join last update installdate for security updates just as a reference to find problematic clients not installing security updates at all
340 | ---- need to convert datetime for older OS from 64bit binary to datetime
341 | left join (
342 | select qfe.ResourceID
343 | --- CUU.CI_ID is not null means the current rollup is installed and we can use that installdate as reference
344 | ,LastInstallTime = max(CASE WHEN CUU.CI_ID is not null then
345 | (
346 | --- the current rollup is installed, but do we have a valid install date? In some cases the installdate seems to be mising. For some Win10 1803 systems for example.
347 | --- "-05" impossible date, since the update could not be released the 5th day of the month (normally). That should indicate the missing date info and give us enough info about the state of the system
348 | --CASE WHEN qfe.InstalledOn0 = '' THEN @UpdatePrefix + '-05 00:00:00' ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101) END
349 | CASE WHEN qfe.InstalledOn0 = '' THEN @CurrentRollupPrefix + '-05 00:00:00' ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101) END
350 | )
351 | else --CUU.CI_ID IS null
352 | (
353 | --- due to some older systems sending datetime as binary like this: 01cc31160e1c4bac and since there is no cumulative update for such systems "CUU.CI_ID is null" should always be valid
354 | --- Found three different date strings so far: MM/dd/yyyy or yyyMMdd or binary
355 | CASE WHEN (LEN(QFE.InstalledOn0) > 10)
356 | THEN CAST((TRY_CONVERT(BIGINT,TRY_CONVERT(VARBINARY(64), '0x' + QFE.InstalledOn0,1)) / 864000000000.0 - 109207) AS DATETIME)
357 | ELSE TRY_CONVERT(datetime,qfe.InstalledOn0,101)
358 | END
359 | ) END)
360 | from v_GS_QUICK_FIX_ENGINEERING QFE
361 | inner join ResourceList on ResourceList.ResourceID = QFE.ResourceID
362 | left join CumulativeUpdates CUU on CUU.ArticleID = QFE.HotFixID0
363 | --where (QFE.Description0 = 'Security Update' or QFE.Description0 is null)
364 | group by qfe.ResourceID
365 | ) UPDINSTDATE on UPDINSTDATE.ResourceID = VRS.ResourceID
366 | ---- join Pending Reboot Info
367 | --left join BGB_LiveData BGBL on BGBL.ResourceID = VRS.ResourceID ---- <- no direct rights assigned and no other view available, using v_CombinedDeviceResources instead
368 | left join v_CombinedDeviceResources BGBL on BGBL.MachineID = VRS.ResourceID
369 |
370 | ---- fix for SQL compat level problem
371 | ---- https://support.microsoft.com/en-us/help/3196320/sql-query-times-out-or-console-slow-on-certain-configuration-manager-d
372 | ---- Force legacy cardinality for SQL server versions before 2016 SP1 (SQL version less than 13.0.4001.0)
373 | --OPTION (QUERYTRACEON 9481)
374 | ---- Force legacy cardinality for SQL server versions 2016 SP1 and higher (SQL version equal or greater than 13.0.4001.0)
375 | --OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))
--------------------------------------------------------------------------------