I have been working on automating the tasks of deploying Windows updates each month. You may think why is there a need for this when SCCM has the Automatic Deployment Rules. Some companies have to review the updates before hand if they have applications that are vulnerable to breaking from windows updates.
This script is the first of a series of steps to achieve this task. This script will automatically create a Software Update Group in SCCM. To further assist the admins, I have also included the ability for the script to generate an excel spreadsheet with a list of the updates that were added to the newly created Update Group. The script then emails the spreadsheet to the designated admin(s). This allows for two things: 1) It reminds the admin that it's that time of the month again, and 2) It allows the admin to review the updates before downloading them, if there are any that need to be removed. That is why I am not automating the package creation portion.
There are three variables that need to be populated: $EmailAddresses, $OperatingSystem, and $Architecture. You can enter multiple email addresses in the $EmailAddresses array. The two optional variables are $UpdateTypes and $Filters. Do not change $Updates.
The script has the ability to specify updates using three different criteria. Set-TimeSpanByMonthsOld lets you specify to include all updates say one month old. My firm deploys updates one month old so that if there are issues in newly released ones, they will typically have been resolved after a month. Say it is currently December, this function will specify all updates from 11/1 to 11/30.
The second criteria is Set-TimeSpanByDatePeriod. This allows for you to specify a specific date range if you are doing a one-off deployment.
The third is Set-TimeSpanAllUpdatesBeforeDate. This allows for you to specify all updates before a specific date to be included.
This script has to be executed on the SCCM server. I have set the script up as a scheduled task that is executed the second Wednesday of every month. You will need to change line 19 to the location of the SCCM module on your server.
I have commented out a few examples within the script.
You can download the script from here.
This script is the first of a series of steps to achieve this task. This script will automatically create a Software Update Group in SCCM. To further assist the admins, I have also included the ability for the script to generate an excel spreadsheet with a list of the updates that were added to the newly created Update Group. The script then emails the spreadsheet to the designated admin(s). This allows for two things: 1) It reminds the admin that it's that time of the month again, and 2) It allows the admin to review the updates before downloading them, if there are any that need to be removed. That is why I am not automating the package creation portion.
There are three variables that need to be populated: $EmailAddresses, $OperatingSystem, and $Architecture. You can enter multiple email addresses in the $EmailAddresses array. The two optional variables are $UpdateTypes and $Filters. Do not change $Updates.
The script has the ability to specify updates using three different criteria. Set-TimeSpanByMonthsOld lets you specify to include all updates say one month old. My firm deploys updates one month old so that if there are issues in newly released ones, they will typically have been resolved after a month. Say it is currently December, this function will specify all updates from 11/1 to 11/30.
The second criteria is Set-TimeSpanByDatePeriod. This allows for you to specify a specific date range if you are doing a one-off deployment.
The third is Set-TimeSpanAllUpdatesBeforeDate. This allows for you to specify all updates before a specific date to be included.
This script has to be executed on the SCCM server. I have set the script up as a scheduled task that is executed the second Wednesday of every month. You will need to change line 19 to the location of the SCCM module on your server.
I have commented out a few examples within the script.
You can download the script from here.
1: <#
2: .NOTES
3: ===========================================================================
4: Created with: SAPIEN Technologies, Inc., PowerShell Studio 2015 v4.2.98
5: Created on: 12/28/2015 9:08 AM
6: Created by: Mick Pletcher
7: Organization:
8: Filename: SoftwareUpdateGroupCreator.ps1
9: ===========================================================================
10: .DESCRIPTION
11: This script will create a new software update group each month. It then
12: generates an email to send to the admin(s) to review what updates
13: were put into the new update group. It does not download the updates so
14: that if an update needs to be removed, it can be done before it is
15: downloaded.
16:
17: #>
18:
19: Import-Module "D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
20:
21: function Get-Updates {
22: param ([String]$Architecture, [String]$OperatingSystem, [String]$Namespace, $Updates )
23:
24: $Updates = @()
25: $Updates = Get-WmiObject -Class SMS_SoftwareUpdate -Namespace $Namespace
26: $Updates = $Updates | where-object { ($_.LocalizedDisplayName -match $OperatingSystem) }
27: If ($Architecture -eq "x86") {
28: $Updates = $Updates | where-object { ($_.LocalizedDisplayName -notmatch "x64") }
29: } elseif ($Architecture -eq "x64") {
30: #$Updates = Get-WmiObject -Class SMS_SoftwareUpdate -Namespace $Namespace | where-object { ($_.LocalizedDisplayName -match $OperatingSystem) -and ($_.LocalizedDisplayName -match "x64-based Systems") }
31: $Updates = Get-WmiObject -Class SMS_SoftwareUpdate -Namespace $Namespace | where-object { ($_.LocalizedDisplayName -match $OperatingSystem) -and ($_.LocalizedDisplayName -match "x64") }
32: }
33: Return $Updates
34: }
35:
36: function Set-Filters {
37: param($Filters, $Updates, $UpdateTypes)
38:
39: #Declare Variables
40: Set-Variable -Name Filter -Scope Local -Force
41: Set-Variable -Name UpdateType -Scope Local -Force
42:
43: foreach ($Filter in $Filters) {
44: $Updates = $Updates | Where-Object { $_.LocalizedDisplayName -notmatch $Filter }
45: }
46: If ($UpdateTypes.Count -ge 1) {
47: foreach ($UpdateType in $UpdateTypes) {
48: $Updates = $Updates | Where-Object { $_.LocalizedCategoryInstanceNames -match $UpdateType }
49: }
50: If ($UpdateTypes -eq "Update") {
51: $Updates = $Updates | Where-Object { $_.LocalizedDisplayName -notmatch "Security Update" }
52: }
53: }
54: return $Updates
55:
56: #Cleanup Variables
57: Remove-Variable -Name Filter -Scope Local -Force
58: Remove-Variable -Name UpdateType -Scope Local -Force
59: }
60:
61: function Set-TimeSpanByMonthsOld {
62: param ($MonthsOld, $Updates)
63:
64: #Declare Local Variables
65: Set-Variable -Name Day -Scope Local -Force
66: Set-Variable -Name Month -Scope Local -Force
67: Set-Variable -Name FirstDayOfMonth -Scope Local -Force
68: Set-Variable -Name LastDayOfMonth -Scope Local -Force
69: Set-Variable -Name Today -Scope Local -Force
70:
71: If ($MonthsOld -ge 1) {
72: $MonthsOld = $MonthsOld * -1
73: }
74: $Today = Get-Date
75: $Month = $Today.AddMonths($MonthsOld)
76: $Day = $Month.Day
77: $FirstDayOfMonth = $Month.AddDays(($Day - 1) * -1)
78: $LastDayOfMonth = [System.DateTime]::DaysInMonth($Month.Year, $Month.Month)
79: $LastDayOfMonth = $LastDayOfMonth - 1
80: $LastDayOfMonth = $FirstDayOfMonth.AddDays($LastDayOfMonth)
81: $FirstDayOfMonth = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($FirstDayOfMonth)
82: $LastDayOfMonth = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($LastDayOfMonth)
83: $Updates = $Updates | Where { ($_.DateCreated -ge $FirstDayOfMonth) -and ($_.DateCreated -le $LastDayOfMonth) }
84: return $Updates
85:
86: #Cleanup Local Variables
87: Remove-Variable -Name Day -Scope Local -Force
88: Remove-Variable -Name Month -Scope Local -Force
89: Remove-Variable -Name FirstDayOfMonth -Scope Local -Force
90: Remove-Variable -Name LastDayOfMonth -Scope Local -Force
91: }
92:
93: function Set-TimeSpanByDatePeriod {
94: param ([String]$StartDate,
95: [String]$EndDate,
96: $Updates)
97:
98: $StartDate = [DateTime]$StartDate
99: $StartDate = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($StartDate)
100: $EndDate = [DateTime]$EndDate
101: $EndDate = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($EndDate)
102: $Updates = $Updates | Where { ($_.DateCreated -ge $StartDate) -and ($_.DateCreated -le $EndDate) }
103: return $Updates
104: }
105:
106: function Set-TimeSpanAllUpdatesBeforeDate {
107: param ([String]$StartDate, $Updates)
108:
109: $StartDate = [DateTime]$StartDate
110: $StartDate = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($StartDate)
111: $Updates = $Updates | Where { $_.DateCreated -lt $StartDate }
112: return $Updates
113: }
114:
115: function CreateSoftwareUpdateGroup {
116: param ($OperatingSystem, $Architecture, $Updates)
117:
118: #Declare Variables
119: Set-Variable -Name Description -Scope Local -Force
120: Set-Variable -Name Month -Scope Local -Force
121: Set-Variable -Name SoftwareUpdateGroupName -Scope Local -Force
122: Set-Variable -Name SoftwareUpdates -Scope Local -Force
123: Set-Variable -Name Temp -Scope Local -Force
124: Set-Variable -Name Update -Scope Local -Force
125: Set-Variable -Name Year -Scope Local -Force
126:
127: $SoftwareUpdates = @()
128: $Year = (Get-Date).Year
129: $Month = Get-Date -format "MMMM"
130: $SoftwareUpdateGroupName = $OperatingSystem + $Architecture + [char]32 + $Month + [char]45 + $Year
131: $Description = $SoftwareUpdateGroupName + [char]32 + "Updates"
132: foreach ($Update in $Updates) {
133: $SoftwareUpdates += ($Update.CI_ID)
134: }
135: cd BNA:
136: $TEMP = New-CMSoftwareUpdateGroup -Name $SoftwareUpdateGroupName -UpdateID $SoftwareUpdates -Description $Description
137: cd c:
138: $SoftwareUpdates = $null
139:
140: #Cleanup Variables
141: Remove-Variable -Name Description -Scope Local -Force
142: Remove-Variable -Name Month -Scope Local -Force
143: Remove-Variable -Name SoftwareUpdateGroupName -Scope Local -Force
144: Remove-Variable -Name SoftwareUpdates -Scope Local -Force
145: Remove-Variable -Name Temp -Scope Local -Force
146: Remove-Variable -Name Update -Scope Local -Force
147: Remove-Variable -Name Year -Scope Local -Force
148: }
149:
150: function ProcessLogFile {
151: param([String]$OperatingSystem, [String]$Architecture)
152:
153: #Declare Local Variables
154: Set-Variable -Name LogFile -Scope Local -Force
155: Set-Variable -Name Month -Scope Local -Force
156: Set-Variable -Name Output -Scope Local -Force
157: Set-Variable -Name temp -Scope Local -Force
158:
159: $Month = Get-Date -format "MMMM"
160: $OperatingSystem = $OperatingSystem -replace '\s',''
161: $LogFile = $env:TEMP + "\" + $OperatingSystem + $Architecture + $Month + "UpdatesReport.csv"
162: if ((Test-Path $LogFile) -eq $true) {
163: Remove-Item $LogFile -Force
164: }
165: if ((Test-Path $LogFile) -eq $false) {
166: $temp = New-Item $LogFile -ItemType file -Force
167: $Output = "Update Name, Article ID, Update Type, Release Date"
168: Out-File -FilePath $LogFile -InputObject $Output -Force -Encoding UTF8
169: }
170: Return $LogFile
171:
172: #Cleanup Local Variables
173: Remove-Variable -Name LogFile -Scope Local -Force
174: Remove-Variable -Name Month -Scope Local -Force
175: Remove-Variable -Name Output -Scope Local -Force
176: Remove-Variable -Name temp -Scope Local -Force
177: }
178:
179: function New-Report {
180: param($EmailAddressList, $Updates, $OperatingSystem, $Architecture)
181:
182: #Declare Variables
183: Set-Variable -Name ArticleID -Scope Local -Force
184: Set-Variable -Name Body -Scope Local -Force
185: Set-Variable -Name DateCreated -Scope Local -Force
186: Set-Variable -Name EmailAddress -Scope Local -Force
187: Set-Variable -Name Month -Scope Local -Force
188: Set-Variable -Name Output -Scope Local -Force
189: Set-Variable -Name Subject -Scope Local -Force
190: Set-Variable -Name Update -Scope Local -Force
191:
192: foreach ($Update in $Updates) {
193: $Update.LocalizedDisplayName = $Update.LocalizedDisplayName -replace ",", ""
194: $ArticleID = "KB" + $Update.ArticleID
195: [String]$DateCreated = [System.Management.ManagementDateTimeConverter]::ToDateTime($Update.DateCreated)
196: If ($Update.LocalizedCategoryInstanceNames -match "Security Updates") {
197: $Output = $Update.LocalizedDisplayName + "," + $ArticleID + ",Security Update," + $DateCreated
198: } elseif (($Update.LocalizedCategoryInstanceNames -notmatch "Security Updates") -and ($Update.LocalizedCategoryInstanceNames -match "Update")) {
199: $Output = $Update.LocalizedDisplayName + "," + $ArticleID + ",Update," + $DateCreated
200: } else {
201: $Output = $Update.LocalizedDisplayName + "," + $ArticleID + ", ," + $DateCreated
202: }
203: Out-File -FilePath $LogFile -InputObject $Output -Append -Force -Encoding UTF8
204: }
205: $Month = Get-Date -format "MMMM"
206: $Subject = $OperatingSystem + $Architecture + [char]32 + $Month + [char]32 + "SCCM Windows Update List"
207: $Body = "List of Windows updates added to the" + $OperatingSystem + $Architecture + [char]32 + $Month + " software update group."
208: foreach ($EmailAddress in $EmailAddressList) {
209: Send-MailMessage -To $EmailAddress -From "engineers@wallerlaw.com" -Subject $Subject -Body $Body -Attachments $LogFile -SmtpServer "smtp.wallerlaw.com"
210: }
211: $EmailAddresses = $null
212:
213: #Cleanup Variables
214: Remove-Variable -Name ArticleID -Scope Local -Force
215: Remove-Variable -Name Body -Scope Local -Force
216: Remove-Variable -Name DateCreated -Scope Local -Force
217: Remove-Variable -Name EmailAddress -Scope Local -Force
218: Remove-Variable -Name Month -Scope Local -Force
219: Remove-Variable -Name Output -Scope Local -Force
220: Remove-Variable -Name Subject -Scope Local -Force
221: Remove-Variable -Name Update -Scope Local -Force
222: }
223:
224: #Declare Variables
225: Set-Variable -Name Architecture -Scope Local -Force
226: Set-Variable -Name EmailAddresses -Scope Local -Force
227: Set-Variable -Name Filters -Scope Local -Force
228: Set-Variable -Name LogFile -Scope Local -Force
229: Set-Variable -Name Namespace -Value root\sms\site_bna -Scope Local -Force
230: Set-Variable -Name OperatingSystem -Scope Local -Force
231: Set-Variable -Name Updates -Scope Local -Force
232: Set-Variable -Name UpdateTypes -Scope Local -Force
233:
234: cls
235: $EmailAddresses = @("mick.pletcher@test.com")
236: $OperatingSystem = "Windows 7" #Windows 7, Windows 8.1, Windows Server 2012 R2
237: $Architecture = "x86" #x86, x64, or null
238: $UpdateTypes = @() #Security Update, Update, Service Pack
239: $Filters = @("Internet Explorer 8", "Internet Explorer 9", "Internet Explorer 10")
240: $Updates = @()
241:
242: $LogFile = ProcessLogFile -OperatingSystem $OperatingSystem -Architecture $Architecture
243: $Updates = Get-Updates -Architecture $Architecture -Namespace $Namespace -OperatingSystem $OperatingSystem -Updates $Updates
244: $Updates = Set-Filters -Filters $Filters -Updates $Updates -UpdateTypes $UpdateTypes
245: $Updates = Set-TimeSpanByMonthsOld -MonthsOld 1 -Updates $Updates
246: #$Updates = Set-TimeSpanByDatePeriod -StartDate "1/1/2015" -EndDate "12/28/2015" -Updates $Updates
247: #$Updates = Set-TimeSpanAllUpdatesBeforeDate -StartDate "9/30/2015" -Updates $Updates
248: CreateSoftwareUpdateGroup -OperatingSystem $OperatingSystem -Updates $Updates -Architecture $Architecture
249: New-Report -EmailAddressList $EmailAddresses -Updates $Updates -OperatingSystem $OperatingSystem -Architecture $Architecture
250: Write-Host
251: Write-Host "Total Number of Updates:"$Updates.Count
252: $Filters = $null
253: $Updates = $null
254: $UpdateTypes = $null
255:
256: #Remove Variables
257: Remove-Variable -Name Architecture -Scope Local -Force
258: Remove-Variable -Name EmailAddresses -Scope Local -Force
259: Remove-Variable -Name Filters -Scope Local -Force
260: Remove-Variable -Name LogFile -Scope Local -Force
261: Remove-Variable -Name Namespace -Scope Local -Force
262: Remove-Variable -Name OperatingSystem -Scope Local -Force
263: Remove-Variable -Name Updates -Scope Local -Force
264: Remove-Variable -Name UpdateTypes -Scope Local -Force
265:
Great script, thanks for sharing. . Do you have a script to deploy software update group to collections, I have to deploy software update group to multiple collections every month, appreciate your help.
ReplyDelete