I found it nice to be able to get a clean, filtered report on what Windows updates got installed during the build process. This allows me to inject those updates into the MDT Packages so they get injected into the image before it is laid down to speed the process up. I had published this tool two years ago and decided to revamp it to also include email functionality. The tool has given me a report, but there were times I forgot to look at it after a build completed. This reminds me by sending the report out via email.
I tried one more way to execute it and it finally worked as shown below:
The command line I used is: powershell.exe -executionpolicy bypass -file <UNC path>\WindowsUpdatesReport.ps1 -Email -From <sender's email address> -To <recipient's email address> -SMTPServer <SMTP server address>
The start in contains the <UNC path> where the script resides.
You can download the file from my GitHub location.
One more thing I wanted to mention is SAPIEN's PowerShell Studio. This made writing this script an absolute breeze. I highly recommend using it.
The way this tool works is by reading the ZTIWindowsUpdate.log file from the c:\minint\smsosd\osdlogs directory and extracting the list of installed Windows Updates. The script filters out everything that is non-windows updates, such as Dell drivers. It also filters out the windows defender updates since those are cumulative and gets updated on a regular basis.
This is a screenshot of what the logs look like when executed and output to the screen:
Here is a screenshot of what the same report looks like when opened up in Excel.
The script extracts the KB article number and description and writes that information to an object. The object is then displayed on the screen and written to a .CSV file. It is sorted by KBArticle number.
The firm I work at uses Dell machines and in doing so I excluded all Dell drivers from the list. There is also an exclusions.txt file it can read from to input items you may want to exclude from the list. I added "*Advanced Micro Devices*" as one item in my TXT file. The exclusions.txt file should reside in the same directory as the script.
The script has been tested when a system is connected to the domain (Final Image) and when it belongs to a workgroup (Reference Image). It works in both instances.
I have pre-populated all parameters, except From, To, and SMTPServer. Those were left blank since you would likely want to populate them at the command line.
Here is an example:
powershell.exe -file WindowsUpdatesReport.ps1 -email -From IT@Testcompany.com -To mickpletcher@testcompany.com -SMTPServer smtp.testcompany.com
I have pre-populated the -OutputFile, -ExclusionsFile, -Subject, and -Body. You can go into the script and change those or decide to override them by defining them at the command line. You could also populate the -From, -To, and -SMTPServer if you like.
Here is a screenshot of how it is setup in the MDT task sequence the first time. This did not work.
And this is a filtered screenshot of how it is setup under as an application install:
I tried one more way to execute it and it finally worked as shown below:
The command line I used is: powershell.exe -executionpolicy bypass -file <UNC path>\WindowsUpdatesReport.ps1 -Email -From <sender's email address> -To <recipient's email address> -SMTPServer <SMTP server address>
The start in contains the <UNC path> where the script resides.
You can download the file from my GitHub location.
One more thing I wanted to mention is SAPIEN's PowerShell Studio. This made writing this script an absolute breeze. I highly recommend using it.
<#
.SYNOPSIS
Generate Windows Updates Report
.DESCRIPTION
This script will extract the list of windows updates installed
during an MDT build.
.PARAMETER OutputFile
File to write the list of installed updates to.
.PARAMETER ExclusionsFile
Text file containing a list of update descriptions to exclude from the report
.PARAMETER Email
Send an email to the specified IT staff with the attached .csv file containing a list of all updates installed during the build process.
.PARAMETER From
Email Sender
.PARAMETER To
Email Recipient
.PARAMETER SMTPServer
SMTPServer
.PARAMETER Subject
Email Subject
.PARAMETER Body
Body contents
.EXAMPLE
powershell.exe -executionpolicy bypass -file WindowsUpdatesReport.ps1 -OutputFile BaseBuild.csv -Path \\NetworkLocation\Directory
.NOTES
===========================================================================
Created with: SAPIEN Technologies, Inc., PowerShell Studio 2017 v5.4.139
Created on: 5/31/2017 10:12 AM
Created by: Mick Pletcher
Filename: WindowsUpdatesReport.ps1
===========================================================================
#>
[CmdletBinding()]
param
(
[ValidateNotNullOrEmpty()][string]$OutputFile = 'WindowsUpdatesReport.csv',
[ValidateNotNullOrEmpty()][string]$ExclusionsFile = 'Exclusions.txt',
[switch]$Email,
[string]$From,
[string]$To,
[string]$SMTPServer,
[string]$Subject = 'Windows Updates Build Report',
[string]$Body = "List of windows updates installed during the build process"
)
function Get-RelativePath {
<#
.SYNOPSIS
Get the relative path
.DESCRIPTION
Returns the location of the currently running PowerShell script
.NOTES
Additional information about the function.
#>
[CmdletBinding()][OutputType([string])]
param ()
$Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"
Return $Path
}
function Remove-OutputFile {
<#
.SYNOPSIS
Delete Output File
.DESCRIPTION
This function deletes the old output file that contains a list of updates that were installed during a build.
#>
[CmdletBinding()]
param ()
#Get the path this script is executing from
$RelativePath = Get-RelativePath
#Define location of the output file
$File = $RelativePath + $OutputFile
If ((Test-Path -Path $File) -eq $true) {
Remove-Item -Path $File -Force
}
}
function Get-Updates {
<#
.SYNOPSIS
Retrieve the list of installed updates
.DESCRIPTION
This function retrieves the list of updates that were installed during the build process
.NOTES
Additional information about the function.
#>
[CmdletBinding()][OutputType([array])]
param ()
$UpdateArray = @()
#Get the path this script is executing from
$RelativePath = Get-RelativePath
#File containing a list of exclusions
$ExclusionsFile = $RelativePath + $ExclusionsFile
#Get list of exclusions from exclusions file
$Exclusions = Get-Content -Path $ExclusionsFile
#Locate the ZTIWindowsUpdate.log file
$FileName = Get-ChildItem -Path $env:HOMEDRIVE"\minint" -filter ztiwindowsupdate.log -recurse
#Get list of all installed updates except for Windows Malicious Software Removal Tool, Definition Update for Windows Defender, and Definition Update for Microsoft Endpoint Protection
$FileContent = Get-Content -Path $FileName.FullName | Where-Object { ($_ -like "*INSTALL*") } | Where-Object { $_ -notlike "*Windows Defender*" } | Where-Object { $_ -notlike "*Endpoint Protection*" } | Where-Object { $_ -notlike "*Windows Malicious Software Removal Tool*" } | Where-Object { $_ -notlike "*Dell*" } | Where-Object { $_ -notlike $Exclusions }
#Filter out all unnecessary lines
$Updates = (($FileContent -replace (" - ", "~")).split("~") | where-object { ($_ -notlike "*LOG*INSTALL*") -and ($_ -notlike "*ZTIWindowsUpdate*") -and ($_ -notlike "*-*-*-*-*") })
foreach ($Update in $Updates) {
#Create object
$Object = New-Object -TypeName System.Management.Automation.PSObject
#Add KB article number to object
$Object | Add-Member -MemberType NoteProperty -Name KBArticle -Value ($Update.split("(")[1]).split(")")[0].Trim()
#Add description of KB article to object
$Description = $Update.split("(")[0]
$Description = $Description -replace (",", " ")
$Object | Add-Member -MemberType NoteProperty -Name Description -Value $Description
#Add the object to the array
$UpdateArray += $Object
}
If ($UpdateArray -ne $null) {
$UpdateArray = $UpdateArray | Sort-Object -Property KBArticle
#Define file to write the report to
$OutputFile = $RelativePath + $OutputFile
$UpdateArray | Export-Csv -Path $OutputFile -NoTypeInformation -NoClobber
}
Return $UpdateArray
}
Clear-Host
#Delete the old report file
Remove-OutputFile
#Get list of installed updates
Get-Updates
If ($Email.IsPresent) {
$RelativePath = Get-RelativePath
$Attachment = $RelativePath + $OutputFile
#Email Updates
Send-MailMessage -From $From -To $To -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment
}
0 comments:
Post a Comment