diff --git a/LatestUpdate/LatestUpdate.json b/LatestUpdate/LatestUpdate.json
new file mode 100644
index 0000000..8ae418d
--- /dev/null
+++ b/LatestUpdate/LatestUpdate.json
@@ -0,0 +1,54 @@
+{
+ "SearchStrings" : {
+ "ServicingStack" : "^Servicing stack update.*",
+ "CumulativeUpdate" : ".*Cumulative Update.*",
+ "AdobeFlash" : ".*Update for Adobe Flash Player.*",
+ "MonthlyRollup" : "(?!Preview of Monthly Rollup)(\\(Monthly Rollup.*\\))",
+ "NetFramework" : "Cumulative Update for \\.NET Framework",
+ "NetFrameworkWindows10" : "Windows 10|Windows Server 2016|Windows Server 2019|Windows Server",
+ "NetFrameworkWindows8" : "Windows 8.1|Server 2012 R2",
+ "NetFrameworkWindows7" : "Windows 7|Server 2008 R2"
+ },
+ "Matches" : {
+ "DownloadUrl" : "(http[s]?\\://download\\.windowsupdate\\.com\\/[^\\'\\\"\"]*)",
+ "DownloadDescription" : "]*>([^<]+)<\\/a>",
+ "Windows10Version" : "([12][056789][01][379])",
+ "Architecture" : "(x86|x64|ARM64)"
+ },
+ "CatalogUris" : {
+ "Search" : "https://www.catalog.update.microsoft.com/Search.aspx?q=",
+ "Download" : "https://www.catalog.update.microsoft.com/DownloadDialog.aspx"
+ },
+ "UpdateFeeds" : {
+ "Windows10" : "https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/6ae59d69-36fc-8e4d-23dd-631d98bf74a9/atom",
+ "Windows8" : "https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/b905caa1-d413-c90c-bed3-20aead901092/atom",
+ "Windows7" : "https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/f825ca23-c7d1-aab8-4513-64980e1c3007/atom",
+ "NetFramework" : "https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/df2c02f6-9610-d46d-2d76-fa570bd8c86a/atom"
+ },
+ "VersionTable" : {
+ "Windows10" : {
+ "1803" : "17134",
+ "1809" : "17763",
+ "1703" : "15063",
+ "1709" : "16299",
+ "1607" : "14393",
+ "1903" : "18362"
+ }
+ },
+ "Languages" : {
+ "Default" : "en-US"
+ },
+ "Architecture" : {
+ "x64" : "x64-based",
+ "x86" : "x86-based",
+ "ARM" : "ARM64-based",
+ "All" : "x86|x64|ARM64"
+ },
+ "Preferences" : {
+ "ErrorAction" : "SilentlyContinue"
+ },
+ "ContentType" : {
+ "html" : "text/html; charset=utf-8",
+ "atom" : "application/atom+xml; charset=utf-8"
+ }
+}
\ No newline at end of file
diff --git a/LatestUpdate/LatestUpdate.psd1 b/LatestUpdate/LatestUpdate.psd1
index 32df978..7a0d840 100644
--- a/LatestUpdate/LatestUpdate.psd1
+++ b/LatestUpdate/LatestUpdate.psd1
@@ -8,134 +8,132 @@
@{
-# Script module or binary module file associated with this manifest.
-RootModule = 'LatestUpdate.psm1'
+ # Script module or binary module file associated with this manifest.
+ RootModule = 'LatestUpdate.psm1'
-# Version number of this module.
-ModuleVersion = '2.4.99'
+ # Version number of this module.
+ ModuleVersion = '3.0.99'
-# Supported PSEditions
-# CompatiblePSEditions = @()
+ # Supported PSEditions
+ # CompatiblePSEditions = @()
-# ID used to uniquely identify this module
-GUID = '1a3f9720-247a-4a25-8120-a164b35856ef'
+ # ID used to uniquely identify this module
+ GUID = '1a3f9720-247a-4a25-8120-a164b35856ef'
-# Author of this module
-Author = 'Aaron Parker'
+ # Author of this module
+ Author = 'Aaron Parker'
-# Company or vendor of this module
-CompanyName = 'stealthpuppy'
+ # Company or vendor of this module
+ CompanyName = 'stealthpuppy'
-# Copyright statement for this module
-Copyright = '(c) 2018-2019 stealthpuppy. All rights reserved.'
+ # Copyright statement for this module
+ Copyright = '(c) 2019 stealthpuppy. All rights reserved.'
-# Description of the functionality provided by this module
-Description = 'A module for retrieving the latest Windows 10 / Windows Server 2016, 2019, Semi Annual Channel Cumulative Updates and Servicing Stack Update, the Monthly Rollups for 8.1 / 7 (and Windows Server 2012 R2, 2008 R2) and the latest Adobe Flash Player updates from the Microsoft Update History page. In addition, the module provides functions for downloading the update file/s locally and importing into a Microsoft Deployment Toolkit deployment share.
+ # Description of the functionality provided by this module
+ Description = 'A module for retrieving the latest Windows 10 / Windows Server 2016, 2019, Semi Annual Channel Cumulative Updates, Servicing Stack Updates, .NET Framework Cumulative Updates, the Monthly Rollups for 8.1 / 7 (and Windows Server 2012 R2, 2008 R2) and the latest Adobe Flash Player updates from the Microsoft Update History page. In addition, the module provides a function for downloading the update files locally.
-This module assists IT Pros in speeding Windows operating system deployments with offline patching in MDT for reference images or PC deployments, or direct application of updates to a WIM.
+This module assists IT Pros in speeding Windows operating system deployments with tracking of Windows updates, offline patching in MDT for reference images or PC deployments, or direct application of updates to a WIM.
-Get-LatestUpdate, Get-LatestServicingStack, Get-LatestFlash and Save-LatestUpdate support PowerShell Core; however, Import-LatestUpdate requires the Microsoft Deployment Toolkit, so requires Windows PowerShell until Microsoft updates the MDT PowerShell module to support PowerShell Core.'
+PowerShell Core is full suppported.'
-# Minimum version of the Windows PowerShell engine required by this module
-PowerShellVersion = '5.0'
+ # Minimum version of the Windows PowerShell engine required by this module
+ PowerShellVersion = '5.0'
-# Name of the Windows PowerShell host required by this module
-# PowerShellHostName = ''
+ # Name of the Windows PowerShell host required by this module
+ # PowerShellHostName = ''
-# Minimum version of the Windows PowerShell host required by this module
-# PowerShellHostVersion = ''
+ # Minimum version of the Windows PowerShell host required by this module
+ # PowerShellHostVersion = ''
-# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
-# DotNetFrameworkVersion = ''
+ # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ # DotNetFrameworkVersion = ''
-# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
-# CLRVersion = ''
+ # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ # CLRVersion = ''
-# Processor architecture (None, X86, Amd64) required by this module
-# ProcessorArchitecture = ''
+ # Processor architecture (None, X86, Amd64) required by this module
+ # ProcessorArchitecture = ''
-# Modules that must be imported into the global environment prior to importing this module
-# RequiredModules = @()
+ # Modules that must be imported into the global environment prior to importing this module
+ # RequiredModules = @()
-# Assemblies that must be loaded prior to importing this module
-# RequiredAssemblies = @()
+ # Assemblies that must be loaded prior to importing this module
+ # RequiredAssemblies = @()
-# Script files (.ps1) that are run in the caller's environment prior to importing this module.
-# ScriptsToProcess = @()
+ # Script files (.ps1) that are run in the caller's environment prior to importing this module.
+ # ScriptsToProcess = @()
-# Type files (.ps1xml) to be loaded when importing this module
-# TypesToProcess = @()
+ # Type files (.ps1xml) to be loaded when importing this module
+ # TypesToProcess = @()
-# Format files (.ps1xml) to be loaded when importing this module
-# FormatsToProcess = @()
+ # Format files (.ps1xml) to be loaded when importing this module
+ # FormatsToProcess = @()
-# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
-# NestedModules = @()
+ # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
+ # NestedModules = @()
-# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
-FunctionsToExport = @('Get-LatestFlash', 'Get-LatestNetFramework',
- 'Get-LatestServicingStack', 'Get-LatestUpdate', 'Import-LatestUpdate',
- 'Save-LatestUpdate')
+ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
+ FunctionsToExport = @('Get-LatestCumulativeUpdate', 'Get-LatestServicingStackUpdate', 'Get-LatestAdobeFlashUpdate', 'Save-LatestUpdate', 'Get-LatestMonthlyRollup', 'Get-LatestNetFrameworkUpdate')
-# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
-CmdletsToExport = @()
+ # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
+ CmdletsToExport = @()
-# Variables to export from this module
-# VariablesToExport = @()
+ # Variables to export from this module
+ # VariablesToExport = @()
-# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
-AliasesToExport = @()
+ # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+ AliasesToExport = 'Get-LatestUpdate', 'Get-LatestFlash', 'Get-LatestServicingStack'
-# DSC resources to export from this module
-# DscResourcesToExport = @()
+ # DSC resources to export from this module
+ # DscResourcesToExport = @()
-# List of all modules packaged with this module
-# ModuleList = @()
+ # List of all modules packaged with this module
+ # ModuleList = @()
-# List of all files packaged with this module
-# FileList = @()
+ # List of all files packaged with this module
+ # FileList = @()
-# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
-PrivateData = @{
+ # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+ PrivateData = @{
- #RepositorySourceLocation of this module
- RepositorySourceLocation = 'https://github.com/aaronparker/LatestUpdate/'
+ #RepositorySourceLocation of this module
+ RepositorySourceLocation = 'https://github.com/aaronparker/LatestUpdate/'
- PSData = @{
+ PSData = @{
- # Tags applied to this module. These help with module discovery in online galleries.
- Tags = 'Windows10','WindowsServer2019','WindowsServer2016','CumulativeUpdate','AdobeFlash','Adobe','Flash','LatestUpdate','MDT','MicrosoftDeploymentToolkit','MonthlyUpdate','ServicingStack'
+ # Tags applied to this module. These help with module discovery in online galleries.
+ Tags = 'Windows10', 'WindowsServer2019', 'WindowsServer2016', 'CumulativeUpdate', 'AdobeFlash', 'Adobe', 'Flash', 'LatestUpdate', 'MDT', 'MicrosoftDeploymentToolkit', 'MonthlyUpdate', 'ServicingStack'
- # A URL to the license for this module.
- LicenseUri = 'https://github.com/aaronparker/LatestUpdate/blob/master/LICENSE'
+ # A URL to the license for this module.
+ LicenseUri = 'https://github.com/aaronparker/LatestUpdate/blob/master/LICENSE'
- # A URL to the main website for this project.
- ProjectUri = 'https://github.com/aaronparker/LatestUpdate/'
+ # A URL to the main website for this project.
+ ProjectUri = 'https://github.com/aaronparker/LatestUpdate/'
- # A URL to an icon representing this module.
- IconUri = 'https://raw.githubusercontent.com/aaronparker/LatestUpdate/master/img/download.png'
+ # A URL to an icon representing this module.
+ IconUri = 'https://raw.githubusercontent.com/aaronparker/LatestUpdate/master/img/download.png'
- # ReleaseNotes of this module
- ReleaseNotes = 'https://docs.stealthpuppy.com/docs/latestupdate/change-log'
+ # ReleaseNotes of this module
+ ReleaseNotes = 'https://docs.stealthpuppy.com/docs/latestupdate/change-log'
- # Prerelease string of this module
- # Prerelease = ''
+ # Prerelease string of this module
+ # Prerelease = ''
- # Flag to indicate whether the module requires explicit user acceptance for install/update/save
- # RequireLicenseAcceptance = $false
+ # Flag to indicate whether the module requires explicit user acceptance for install/update/save
+ # RequireLicenseAcceptance = $false
- # External dependent modules of this module
- # ExternalModuleDependencies = @()
+ # External dependent modules of this module
+ # ExternalModuleDependencies = @()
- } # End of PSData hashtable
+ } # End of PSData hashtable
-} # End of PrivateData hashtable
+ } # End of PrivateData hashtable
-# HelpInfo URI of this module
-# HelpInfoURI = ''
+ # HelpInfo URI of this module
+ # HelpInfoURI = ''
-# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
-# DefaultCommandPrefix = ''
+ # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+ # DefaultCommandPrefix = ''
}
diff --git a/LatestUpdate/LatestUpdate.psm1 b/LatestUpdate/LatestUpdate.psm1
index 953bf71..9f5189a 100644
--- a/LatestUpdate/LatestUpdate.psm1
+++ b/LatestUpdate/LatestUpdate.psm1
@@ -1,16 +1,19 @@
# Get public and private function definition files
-$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
-$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
+$publicRoot = Join-Path -Path $PSScriptRoot -ChildPath "Public"
+$privateRoot = Join-Path -Path $PSScriptRoot -ChildPath "Private"
+$public = @( Get-ChildItem -Path (Join-Path $publicRoot "*.ps1") -ErrorAction SilentlyContinue )
+$private = @( Get-ChildItem -Path (Join-Path $privateRoot "*.ps1") -ErrorAction SilentlyContinue )
# Dot source the files
-ForEach ($import in @($Public + $Private)) {
+ForEach ($import in @($public + $private)) {
Try {
. $import.fullname
}
Catch {
- Write-Error -Message "Failed to import function $($import.fullname): $_"
+ Write-Warning -Message "Failed to import function $($import.fullname)."
+ Throw $_.Exception.Message
}
}
# Export the Public modules
-Export-ModuleMember -Function $Public.Basename
+Export-ModuleMember -Function $public.Basename -Alias *
diff --git a/LatestUpdate/Private/Add-Property.ps1 b/LatestUpdate/Private/Add-Property.ps1
new file mode 100644
index 0000000..4c42ec0
--- /dev/null
+++ b/LatestUpdate/Private/Add-Property.ps1
@@ -0,0 +1,36 @@
+Function Add-Property {
+ <#
+ .SYNOPSIS
+ Adds a property to a PSObject by querying another property
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.PSObject] $InputObject,
+
+ [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $Property,
+
+ [Parameter(Mandatory = $True, Position = 2, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $NewPropertyName,
+
+ [Parameter(Mandatory = $True, Position = 3, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $MatchPattern
+ )
+
+ ForEach ($object in $InputObject) {
+ $value = $object | Select-Object -ExpandProperty $Property | `
+ Select-String -AllMatches -Pattern $MatchPattern | `
+ ForEach-Object { $_.Matches.Value }
+ If ($value.Count -ge 2) {
+ $value = $value | Select-Object -Last 1
+ }
+ $object | Add-Member -NotePropertyName $NewPropertyName -NotePropertyValue $value
+ Write-Output -InputObject $object
+ }
+}
diff --git a/LatestUpdate/Private/ConvertTo-Hashtable.ps1 b/LatestUpdate/Private/ConvertTo-Hashtable.ps1
new file mode 100644
index 0000000..b5aefc7
--- /dev/null
+++ b/LatestUpdate/Private/ConvertTo-Hashtable.ps1
@@ -0,0 +1,52 @@
+Function ConvertTo-Hashtable {
+ <#
+ .SYNOPSIS
+ Converts a PSCustomObject into a hashtable for Windows PowerShell
+
+ .NOTES
+ Author: Adam Bertram
+ Link: https://4sysops.com/archives/convert-json-to-a-powershell-hash-table
+ #>
+ [CmdletBinding()]
+ [OutputType('hashtable')]
+ Param (
+ [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)]
+ $InputObject
+ )
+
+ Process {
+ ## Return null if the input is null. This can happen when calling the function
+ ## recursively and a property is null
+ If ($Null -eq $InputObject) {
+ Return $Null
+ }
+
+ ## Check if the input is an array or collection. If so, we also need to convert
+ ## those types into hash tables as well. This function will convert all child
+ ## objects into hash tables (if applicable)
+ If ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
+ $collection = @(
+ ForEach ($object in $InputObject) {
+ ConvertTo-Hashtable -InputObject $object
+ }
+ )
+
+ ## Return the array but don't enumerate it because the object may be pretty complex
+ Write-Output -NoEnumerate -InputObject $collection
+ }
+ ElseIf ($InputObject -is [psobject]) {
+ ## If the object has properties that need enumeration
+ ## Convert it to its own hash table and return it
+ $hash = @{ }
+ ForEach ($property in $InputObject.PSObject.Properties) {
+ $hash[$property.Name] = ConvertTo-Hashtable -InputObject $property.Value
+ }
+ $hash
+ }
+ Else {
+ ## If the object isn't an array, collection, or other object, it's already a hash table
+ ## So just return it.
+ $InputObject
+ }
+ }
+}
diff --git a/LatestUpdate/Private/Get-KbUpdateArray.ps1 b/LatestUpdate/Private/Get-KbUpdateArray.ps1
deleted file mode 100644
index 166b56a..0000000
--- a/LatestUpdate/Private/Get-KbUpdateArray.ps1
+++ /dev/null
@@ -1,36 +0,0 @@
-Function Get-KbUpdateArray {
- <#
- .SYNOPSIS
- Constructs a table with KB article, Update Id number and Update description from provided HTML links
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Links
- An object containing links filtered from a HTML object.
- #>
- [CmdletBinding()]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [PSCustomObject] $Links,
-
- [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String] $KB
- )
-
- # RegEx to grab text within tags in HTML
- [regex] $rx = "]*>([^<]+)<\/a>"
-
- #region Contruct a table with KB, Id and Update description
- Write-Verbose -Message "Contructing temporary table with KB, ID and URL."
- $table = $Links | Where-Object ID -match '_link' | `
- Select-Object @{n = "KB"; e = {"KB$KB"}}, @{n = "Id"; e = {$_.id.Replace('_link', '')}}, `
- @{n = "Note"; e = {(($_.outerHTML -replace $rx, '$1').TrimStart()).TrimEnd()}}
- #endregion
-
- # Return the table to the pipeline
- Write-Output -InputObject $table
-}
diff --git a/LatestUpdate/Private/Get-ModuleResource.ps1 b/LatestUpdate/Private/Get-ModuleResource.ps1
new file mode 100644
index 0000000..7021339
--- /dev/null
+++ b/LatestUpdate/Private/Get-ModuleResource.ps1
@@ -0,0 +1,41 @@
+Function Get-ModuleResource {
+ <#
+ .SYNOPSIS
+ Reads the module strings from the JSON file and returns a hashtable.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNull()]
+ [ValidateScript( { If (Test-Path -Path $_ -PathType 'Leaf') { $True } Else { Throw "Cannot find file $_" } })]
+ [System.String] $Path = (Join-Path -Path $MyInvocation.MyCommand.Module.ModuleBase -ChildPath "LatestUpdate.json")
+ )
+
+ try {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): read module resource strings from [$Path]"
+ $content = Get-Content -Path $Path -Raw -ErrorAction SilentlyContinue
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to read from: $Path."
+ Throw $_.Exception.Message
+ }
+
+ try {
+ If (Test-PSCore) {
+ $resourceStringsTable = $content | ConvertFrom-Json -AsHashtable -ErrorAction SilentlyContinue
+ }
+ Else {
+ $resourceStringsTable = $content | ConvertFrom-Json -ErrorAction SilentlyContinue | ConvertTo-Hashtable
+ }
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to convert strings to required object."
+ Throw $_.Exception.Message
+ }
+ finally {
+ If ($Null -ne $resourceStringsTable) {
+ Write-Output -InputObject $resourceStringsTable
+ }
+ }
+}
diff --git a/LatestUpdate/Private/Get-RxString.ps1 b/LatestUpdate/Private/Get-RxString.ps1
deleted file mode 100644
index a2a4b18..0000000
--- a/LatestUpdate/Private/Get-RxString.ps1
+++ /dev/null
@@ -1,36 +0,0 @@
-Function Get-RxString {
- <#
- .SYNOPSIS
- Return matching sub-string from a supplied string..
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER String
- A string to search.
-
- .PARAMETER RegEx
- A regular expression to match.
- #>
- [CmdletBinding()]
- [OutputType([String])]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [string] $String,
-
- [Parameter(Mandatory = $True, Position = 1)]
- [ValidateNotNullOrEmpty()]
- [RegEx] $RegEx
- )
-
- # Extract sub-string from $String via the RegEx and return on the pipeline
- $String -match $RegEx > $Null
- If ($Null -ne $Matches[1]) {
- Write-Output $Matches[1]
- }
-
- # Alternative method for extracting sub-string
- # [regex]::match($String, $RegEx).Groups[1].Value
-}
diff --git a/LatestUpdate/Private/Get-UpdateAdobeFlash.ps1 b/LatestUpdate/Private/Get-UpdateAdobeFlash.ps1
new file mode 100644
index 0000000..2a9ac5c
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateAdobeFlash.ps1
@@ -0,0 +1,48 @@
+Function Get-UpdateAdobeFlash {
+ <#
+ .SYNOPSIS
+ Builds an object with the Adobe Flash Player Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlNode] $UpdateFeed
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # Filter object matching desired update type
+ $updateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($item in $UpdateFeed.feed.entry) {
+ If ($item.title -match $resourceStrings.SearchStrings.AdobeFlash) {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): matched item [$($item.title)]"
+ $PSObject = [PSCustomObject] @{
+ Title = $item.title
+ ID = $item.id
+ Updated = $item.updated
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
+ }
+
+ # Filter and select the most current update
+ If ($updateList.Count -ge 1) {
+ $sortedUpdateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($update in $updateList) {
+ $PSObject = [PSCustomObject] @{
+ Title = $update.title
+ ID = "KB{0}" -f ($update.id).Split(":")[2]
+ Updated = ([DateTime]::Parse($update.updated))
+ }
+ $sortedUpdateList.Add($PSObject) | Out-Null
+ }
+ $latestUpdate = $sortedUpdateList | Sort-Object -Property Updated -Descending | Select-Object -First 1
+ Write-Verbose -Message "$($MyInvocation.MyCommand): selected item [$($latestUpdate.title)]"
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $latestUpdate
+}
diff --git a/LatestUpdate/Private/Get-UpdateCatalogDownloadInfo.ps1 b/LatestUpdate/Private/Get-UpdateCatalogDownloadInfo.ps1
new file mode 100644
index 0000000..c89ea29
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateCatalogDownloadInfo.ps1
@@ -0,0 +1,78 @@
+Function Get-UpdateCatalogDownloadInfo {
+ <#
+ .SYNOPSIS
+ Builds an object with the update notes and download details.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param(
+ [Parameter(Mandatory = $True)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $UpdateId,
+
+ [Parameter(Mandatory = $False)]
+ [System.String] $Architecture = 'x64|x86|ARM64',
+
+ [Parameter(Mandatory = $False)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $OS = "Windows 10|Windows Server"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # Search the Update Catalog for the specific update KB
+ $searchResult = Invoke-UpdateCatalogSearch -UpdateId $UpdateId
+
+ If ($Null -ne $searchResult) {
+ # Determine link id's and update description
+ $UpdateCatalogItems = ($searchResult.Links | Where-Object { $_.Id -match "_link" })
+ ForEach ($UpdateCatalogItem in $UpdateCatalogItems) {
+ If (($UpdateCatalogItem.outerHTML -match $Architecture) -and ($UpdateCatalogItem.outerHTML -match $OS)) {
+ $CurrentUpdateDescription = ($UpdateCatalogItem.outerHTML -replace $resourceStrings.Matches.DownloadDescription, '$1').TrimStart().TrimEnd()
+ $CurrentUpdateLinkID = $UpdateCatalogItem.id.Replace("_link", "")
+ Write-Verbose -Message "$($MyInvocation.MyCommand): match item [$CurrentUpdateDescription]"
+ }
+
+ # Construct update catalog object that will be used to call update catalog download API
+ $UpdateCatalogData = [PSCustomObject] @{
+ KB = $UpdateId
+ LinkID = $CurrentUpdateLinkID
+ Description = $CurrentUpdateDescription
+ }
+
+ # Construct an ordered hashtable containing the update ID data and convert to JSON
+ $UpdateCatalogTable = [ordered] @{
+ Size = 0
+ Languages = $resourceStrings.Languages.Default
+ UidInfo = $UpdateCatalogData.LinkID
+ UpdateID = $UpdateCatalogData.LinkID
+ }
+ $UpdateCatalogJSON = $UpdateCatalogTable | ConvertTo-Json -Compress
+
+ # Construct body object for web request call
+ $Body = @{
+ UpdateIDs = "[$($UpdateCatalogJSON)]"
+ }
+
+ # Call update catalog download dialog using a rest call
+ $updateDownload = Invoke-UpdateCatalogDownloadDialog -Body $Body
+
+ # Match specific update
+ If ($Null -ne $updateDownload) {
+ $updateDownloadURL = $updateDownload | Select-Object -ExpandProperty Content | `
+ Select-String -AllMatches -Pattern $resourceStrings.Matches.DownloadUrl | `
+ ForEach-Object { $_.Matches.Value }
+ Write-Verbose -Message "$($MyInvocation.MyCommand): extract URL [$updateDownloadURL]"
+
+ $UpdateCatalogDownloadItem = [PSCustomObject] @{
+ KB = $UpdateCatalogData.KB
+ Note = $CurrentUpdateDescription
+ URL = $updateDownloadURL
+ }
+
+ Write-Output -InputObject $UpdateCatalogDownloadItem
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Private/Get-UpdateCatalogLink.ps1 b/LatestUpdate/Private/Get-UpdateCatalogLink.ps1
deleted file mode 100644
index d8740e3..0000000
--- a/LatestUpdate/Private/Get-UpdateCatalogLink.ps1
+++ /dev/null
@@ -1,39 +0,0 @@
-Function Get-UpdateCatalogLink {
- <#
- .SYNOPSIS
- Returns contents of a KB search from the Microsoft Update Catalog
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER KB
- Knowledgebase article string to search the catalog for.
- #>
- [CmdletBinding(DefaultParameterSetName="KB")]
- [CmdLetBinding()]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ParameterSetName = "KB")]
- [ValidateNotNullOrEmpty()]
- [String] $KB,
-
- [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline = $True, ParameterSetName = "SearchString")]
- [ValidateNotNullOrEmpty()]
- [String] $SearchString = "KB" + $KB
- )
-
- try {
- $uri = "http://www.catalog.update.microsoft.com/Search.aspx?q=$SearchString"
- Write-Verbose -Message "Searching $uri."
- $kbPage = Invoke-WebRequest -Uri $uri -UseBasicParsing -ErrorAction SilentlyContinue
- }
- catch {
- # Write warnings if we can't read values
- If ($Null -eq $kbPage) { Write-Warning -Message "Page object is Null. Unable to read KB details from the Catalog." }
- If ($Null -eq $kbPage.InputFields) { Write-Warning -Message " Page input fields are Null. Unable to read button details from the Catalog KB page." }
- Throw $_
- }
- finally {
- Write-Output $kbPage
- }
-}
diff --git a/LatestUpdate/Private/Get-UpdateCumulative.ps1 b/LatestUpdate/Private/Get-UpdateCumulative.ps1
new file mode 100644
index 0000000..a56ecba
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateCumulative.ps1
@@ -0,0 +1,54 @@
+Function Get-UpdateCumulative {
+ <#
+ .SYNOPSIS
+ Builds an object with the Cumulative Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlNode] $UpdateFeed,
+
+ [Parameter(Mandatory = $False, Position = 1, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $Build
+ )
+
+ # Filter object matching desired update type
+ [regex] $rxB = "$Build.(\d+)"
+ $updateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($item in $UpdateFeed.feed.entry) {
+ If ($item.title -match $rxB) {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): matched item [$($item.title)]"
+ $BuildVersion = [regex]::Match($item.title, $rxB).Value
+ $PSObject = [PSCustomObject] @{
+ Title = $item.title
+ ID = $item.id
+ Build = $BuildVersion
+ Updated = $item.updated
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
+ }
+
+ # Filter and select the most current update
+ If ($updateList.Count -ge 1) {
+ $sortedUpdateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($update in $updateList) {
+ $PSObject = [PSCustomObject] @{
+ Title = $update.title
+ ID = "KB{0}" -f ($update.id).Split(":")[2]
+ Build = $update.Build.Split(".")[0]
+ Revision = [int]($update.Build.Split(".")[1])
+ Updated = ([DateTime]::Parse($update.updated))
+ }
+ $sortedUpdateList.Add($PSObject) | Out-Null
+ }
+ $latestUpdate = $sortedUpdateList | Sort-Object -Property Revision -Descending | Select-Object -First 1
+ Write-Verbose -Message "$($MyInvocation.MyCommand): selected item [$($latestUpdate.title)]"
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $latestUpdate
+}
diff --git a/LatestUpdate/Private/Get-UpdateDownloadArray.ps1 b/LatestUpdate/Private/Get-UpdateDownloadArray.ps1
deleted file mode 100644
index 73aef1b..0000000
--- a/LatestUpdate/Private/Get-UpdateDownloadArray.ps1
+++ /dev/null
@@ -1,104 +0,0 @@
-Function Get-UpdateDownloadArray {
- <#
- .SYNOPSIS
- Constructs a table with KB article, Update Id number and Update description from provided HTML links
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Links
- An object containing links filtered from a HTML object.
- #>
- [CmdletBinding()]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [PSCustomObject] $IdTable
- )
-
- # Search strings
- [string] $win10 = "Windows 10.*"
- [string] $rxArch = "\s+([a-zA-Z0-9]+)-based"
- [regex] $rx10 = "([1-2][0-9][0-1][0-9])\s"
- $editions = @{
- "Windows Server 2019" = "1809"
- "Windows Server 2016" = "1607"
- "Windows Server 2012 R2" = "2012 R2"
- "Windows Server 2012(?!\sR2)" = "2012"
- "Windows Server 2008 R2" = "2008R2"
- "Windows 8.1" = "8.1"
- "Windows Embedded 8 Standard" = "8Embedded"
- "Windows 7" = "7"
- "Windows Embedded Standard 7" = "7Embedded"
- }
- $architectures = @{
- "Windows Server 2019" = "x64"
- "Windows Server 2016" = "x64"
- "Windows Server 2012 R2" = "x64"
- "Windows Server 2012(?!\sR2)" = "x64"
- "Windows Server 2008 R2" = "x64"
- "Windows 8.1" = "x86"
- "Windows Embedded 8 Standard" = "x86"
- "Windows 7" = "x86"
- "Windows Embedded Standard 7" = "x86"
- }
-
- $output = @()
- ForEach ($idItem in $IdTable) {
- try {
- Write-Verbose -Message "Checking Microsoft Update Catalog for Id: $($idItem.id)."
- $post = @{ size = 0; updateID = $idItem.id; uidInfo = $idItem.id } | ConvertTo-Json -Compress
- $postBody = @{ updateIDs = "[$post]" }
- $url = Invoke-WebRequest -Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' `
- -Method Post -Body $postBody -UseBasicParsing -ErrorAction SilentlyContinue | `
- Select-Object -ExpandProperty Content | `
- Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" | `
- ForEach-Object { $_.matches.value }
- }
- catch {
- Throw "Failed to parse Microsoft Update Catalog for Id: $($idItem.id)."
- Break
- }
- finally {
- Write-Verbose -Message "Adding $url to output."
- $newItem = New-Object PSCustomObject
- $newItem | Add-Member -type NoteProperty -Name 'KB' -Value $idItem.KB
-
- # Add custom version note and check architecture based on description returned from page
- $arch = Get-RxString -String $idItem.Note -RegEx $rxArch
-
- If ($idItem.Note -match $win10) {
- $version = Get-RxString -String $idItem.Note -RegEx $rx10
- If ($Null -eq $arch) { $arch = "x86" }
- }
- Else {
- ForEach ($key in $editions.Keys) {
- If ($idItem.Note -match ([regex] $key)) {
- Write-Verbose "Matched: $key"
- $version = $editions[$key]
- If ($Null -eq $arch) { $arch = $architectures[$key] }
- }
- }
- }
-
- $newItem | Add-Member -type NoteProperty -Name 'Arch' -Value $arch
- $newItem | Add-Member -type NoteProperty -Name 'Version' -Value $version
- $newItem | Add-Member -type NoteProperty -Name 'Note' -Value $idItem.Note
-
- # Filter URL to ensure only .MSU updates are returned
- If (Test-PSCore) {
- $download = $url | Where-Object { (Split-Path -Path $_ -Extension) -match ".msu" }
- }
- Else {
- $download = $url | Where-Object { ([IO.Path]::GetExtension($_)) -match ".msu" }
- }
- $newItem | Add-Member -type NoteProperty -Name 'URL' -Value $download
-
- # Add item to the output array
- $output += $newItem
- }
- }
-
- Write-Output $output
-}
diff --git a/LatestUpdate/Private/Get-UpdateFeed.ps1 b/LatestUpdate/Private/Get-UpdateFeed.ps1
index 40a8f32..aa482a2 100644
--- a/LatestUpdate/Private/Get-UpdateFeed.ps1
+++ b/LatestUpdate/Private/Get-UpdateFeed.ps1
@@ -1,57 +1,66 @@
Function Get-UpdateFeed {
<#
.SYNOPSIS
- Gets the Microsoft update RSS feed and returns the XML
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER UpdateFeed
- URI to the feed.
+ Returns an XML object from the specified update feed
#>
- [CmdletBinding()]
+ [OutputType([System.Xml.XmlNode])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
+ [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
- [String] $UpdateFeed
+ [System.String] $Uri
)
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
- #region Find the KB Article Number
- #! Fix for Invoke-WebRequest creating BOM in XML files; Handle Temp locations on Windows, macOS / Linux
- try {
- If (Test-Path env:Temp) {
- $tempDir = $env:Temp
- }
- ElseIf (Test-Path env:TMPDIR) {
- $tempDir = $env:TMPDIR
- }
- $tempFile = Join-Path -Path $tempDir -ChildPath ([System.IO.Path]::GetRandomFileName())
- Write-Verbose -Message "Downloading feed of updates $UpdateFeed."
- Invoke-WebRequest -Uri $UpdateFeed -ContentType 'application/atom+xml; charset=utf-8' `
- -UseBasicParsing -OutFile $tempFile -ErrorAction SilentlyContinue
+ # Fix for Invoke-WebRequest creating BOM in XML files; Handle Temp locations on Windows, macOS / Linux
+ If (Test-Path -Path env:Temp) {
+ $tempDir = $env:Temp
}
- catch {
- Throw $_
+ ElseIf (Test-Path -Path env:TMPDIR) {
+ $tempDir = $env:TMPDIR
}
-
- # Import the XML from the feed into a variable and delete the temp file
+ $tempFile = Join-Path -Path $tempDir -ChildPath ([System.IO.Path]::GetRandomFileName())
+
try {
- Write-Verbose -Message "Reading RSS XML from $tempFile."
- $feedXML = [xml] (Get-Content -Path $tempFile -ErrorAction SilentlyContinue)
+ $params = @{
+ Uri = $Uri
+ OutFile = $tempFile
+ ContentType = $resourceStrings.ContentType.atom
+ UserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
+ UseBasicParsing = $True
+ ErrorAction = $resourceStrings.Preferences.ErrorAction
+ }
+ Invoke-WebRequest @params
}
- catch {
- Write-Error "Failed to read XML from $tempFile."
+ catch [System.Net.WebException] {
+ Write-Warning -Message ($($MyInvocation.MyCommand))
+ Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message))
}
- try {
- Write-Verbose -Message "Deleting $tempFile."
- Remove-Item -Path $tempFile -ErrorAction SilentlyContinue
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to retreive the update feed: $Uri."
+ Throw $_.Exception.Message
}
- catch {
- Write-Warning -Message "Failed to remove file $tempFile."
+
+ # Import the XML from the feed into a variable and delete the temp file
+ If (Test-Path -Path $tempFile) {
+ try {
+ [xml] $xml = Get-Content -Path $tempFile -Raw -ErrorAction $resourceStrings.Preferences.ErrorAction
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to read XML from file: $tempFile."
+ Throw $_.Exception.Message
+ }
+ try {
+ Remove-Item -Path $tempFile -Force -ErrorAction $resourceStrings.Preferences.ErrorAction
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to remove file: $tempFile."
+ }
}
- #! End fix
- # Return the XML to the pipeline
- Write-Output $feedXML
+ If ($Null -ne $xml) {
+ Write-Output -InputObject $xml
+ }
}
diff --git a/LatestUpdate/Private/Get-UpdateMonthly.ps1 b/LatestUpdate/Private/Get-UpdateMonthly.ps1
new file mode 100644
index 0000000..88100bc
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateMonthly.ps1
@@ -0,0 +1,48 @@
+Function Get-UpdateMonthly {
+ <#
+ .SYNOPSIS
+ Builds an object with the Windows 8.1/7 Monthly Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlNode] $UpdateFeed
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # Filter object matching desired update type
+ $updateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($item in $UpdateFeed.feed.entry) {
+ If ($item.title -match $resourceStrings.SearchStrings.MonthlyRollup) {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): matched item [$($item.title)]"
+ $PSObject = [PSCustomObject] @{
+ Title = $item.title
+ ID = $item.id
+ Updated = $item.updated
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
+ }
+
+ # Filter and select the most current update
+ If ($updateList.Count -ge 1) {
+ $sortedUpdateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($update in $updateList) {
+ $PSObject = [PSCustomObject] @{
+ Title = $update.title
+ ID = "KB{0}" -f ($update.id).Split(":")[2]
+ Updated = ([DateTime]::Parse($update.updated))
+ }
+ $sortedUpdateList.Add($PSObject) | Out-Null
+ }
+ $latestUpdate = $sortedUpdateList | Sort-Object -Property Updated -Descending | Select-Object -First 1
+ Write-Verbose -Message "$($MyInvocation.MyCommand): selected item [$($latestUpdate.title)]"
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $latestUpdate
+}
diff --git a/LatestUpdate/Private/Get-UpdateNetFramework.ps1 b/LatestUpdate/Private/Get-UpdateNetFramework.ps1
new file mode 100644
index 0000000..500cd34
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateNetFramework.ps1
@@ -0,0 +1,47 @@
+Function Get-UpdateNetFramework {
+ <#
+ .SYNOPSIS
+ Builds an object with the .NET Framework Cumulative Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlNode] $UpdateFeed
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # Filter object matching desired update type
+ $updateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($item in $UpdateFeed.feed.entry) {
+ If ($item.title -match $resourceStrings.SearchStrings.NetFramework) {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): matched item [$($item.title)]"
+ $PSObject = [PSCustomObject] @{
+ Title = $item.title
+ ID = $item.id
+ Updated = $item.updated
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
+ }
+
+ # Filter and select the most current update
+ If ($updateList.Count -ge 1) {
+ $sortedUpdateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($update in $updateList) {
+ $PSObject = [PSCustomObject] @{
+ Title = $update.title
+ ID = "KB{0}" -f ($update.id).Split(":")[2]
+ Updated = ([DateTime]::Parse($update.updated))
+ }
+ $sortedUpdateList.Add($PSObject) | Out-Null
+ }
+ $latestUpdate = $sortedUpdateList | Sort-Object -Property Updated -Descending
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $latestUpdate
+}
diff --git a/LatestUpdate/Private/Get-UpdateServicingStack.ps1 b/LatestUpdate/Private/Get-UpdateServicingStack.ps1
new file mode 100644
index 0000000..67a5719
--- /dev/null
+++ b/LatestUpdate/Private/Get-UpdateServicingStack.ps1
@@ -0,0 +1,53 @@
+Function Get-UpdateServicingStack {
+ <#
+ .SYNOPSIS
+ Builds an object with the Servicing Stack Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlNode] $UpdateFeed,
+
+ [Parameter(Mandatory = $False, Position = 1, ValueFromPipeline)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $Version
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # Filter object matching desired update type
+ $updateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($item in $UpdateFeed.feed.entry) {
+ If (($item.title -match $resourceStrings.SearchStrings.ServicingStack) -and ($item.title -match ".*$($Version).*")) {
+ Write-Verbose -Message "$($MyInvocation.MyCommand): matched item [$($item.title)]"
+ $PSObject = [PSCustomObject] @{
+ Title = $item.title
+ ID = $item.id
+ Version = $Version
+ Updated = $item.updated
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
+ }
+
+ # Filter and select the most current update
+ If ($updateList.Count -ge 1) {
+ $sortedUpdateList = New-Object -TypeName System.Collections.ArrayList
+ ForEach ($update in $updateList) {
+ $PSObject = [PSCustomObject] @{
+ Title = $update.title
+ ID = "KB{0}" -f ($update.id).Split(":")[2]
+ Updated = ([DateTime]::Parse($update.updated))
+ }
+ $sortedUpdateList.Add($PSObject) | Out-Null
+ }
+ $latestUpdate = $sortedUpdateList | Sort-Object -Property Updated -Descending | Select-Object -First 1
+ Write-Verbose -Message "$($MyInvocation.MyCommand): selected item [$($latestUpdate.title)]"
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $latestUpdate
+}
diff --git a/LatestUpdate/Private/Get-ValidPath.ps1 b/LatestUpdate/Private/Get-ValidPath.ps1
deleted file mode 100644
index a55efdf..0000000
--- a/LatestUpdate/Private/Get-ValidPath.ps1
+++ /dev/null
@@ -1,23 +0,0 @@
-Function Get-ValidPath {
- <#
- .SYNOPSIS
- Test a file system path and return correct path string.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Path
- A directory path that the function will validate and return.
- #>
- [CmdletBinding()]
- [OutputType([String])]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
- [ValidateNotNullOrEmpty()]
- [Alias("PSPath")]
- [string] $Path
- )
- $output = ((Get-Item $Path).FullName).TrimEnd("\")
- Write-Output $output
-}
diff --git a/LatestUpdate/Private/Import-MdtModule.ps1 b/LatestUpdate/Private/Import-MdtModule.ps1
deleted file mode 100644
index 05f93c2..0000000
--- a/LatestUpdate/Private/Import-MdtModule.ps1
+++ /dev/null
@@ -1,24 +0,0 @@
-Function Import-MdtModule {
- <#
- .SYNOPSIS
- Imports the MDT PowerShell module.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
- #>
-
- # Find the location to the MDT PowerShell module
- $InstallDir = Get-ValidPath -Path $((Get-ItemProperty "HKLM:SOFTWARE\Microsoft\Deployment 4" -ErrorAction SilentlyContinue).Install_Dir)
- $MdtPath = Join-Path $installDir "bin"
- $MdtModule = Resolve-Path -Path (Join-Path $MdtPath "MicrosoftDeploymentToolkit.psd1")
-
- Try {
- Import-Module -Name $MdtModule -ErrorAction SilentlyContinue
- }
- Catch {
- Throw "Could not load MDT PowerShell Module. Please make sure that the MDT Workbench is installed correctly."
- }
-
- Write-Output $?
-}
diff --git a/LatestUpdate/Private/Invoke-UpdateCatalogDownloadDialog.ps1 b/LatestUpdate/Private/Invoke-UpdateCatalogDownloadDialog.ps1
new file mode 100644
index 0000000..82c1ac1
--- /dev/null
+++ b/LatestUpdate/Private/Invoke-UpdateCatalogDownloadDialog.ps1
@@ -0,0 +1,48 @@
+Function Invoke-UpdateCatalogDownloadDialog {
+ <#
+ .SYNOPSIS
+ Searches the Microsoft Update Catalog for the specific update ID to retrieve the notes and download details.
+ #>
+ [OutputType([Microsoft.PowerShell.Commands.WebResponseObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param(
+ [Parameter(Mandatory = $True)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable] $Body
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ If ($Null -ne $resourceStrings) {
+ try {
+ $params = @{
+ Uri = $resourceStrings.CatalogUris.Download
+ Body = $Body
+ ContentType = $resourceStrings.ContentType.html
+ UserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
+ UseBasicParsing = $True
+ ErrorAction = $resourceStrings.Preferences.ErrorAction
+ }
+ $downloadResult = Invoke-WebRequest @params
+ }
+ catch [System.Net.WebException] {
+ Write-Warning -Message ($($MyInvocation.MyCommand))
+ Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message))
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to search the catalog download: $Uri."
+ Throw $_.Exception.Message
+ }
+
+ If ($downloadResult.StatusCode -eq "200") {
+ Write-Output -InputObject $downloadResult
+ }
+ Else {
+ Write-Warning -Message "$($MyInvocation.MyCommand): no valid response."
+ }
+ }
+ Else {
+ Write-Warning -Message "$($MyInvocation.MyCommand): unable to retreive Update Catalog download."
+ }
+}
diff --git a/LatestUpdate/Private/Invoke-UpdateCatalogSearch.ps1 b/LatestUpdate/Private/Invoke-UpdateCatalogSearch.ps1
new file mode 100644
index 0000000..ec5ed0f
--- /dev/null
+++ b/LatestUpdate/Private/Invoke-UpdateCatalogSearch.ps1
@@ -0,0 +1,48 @@
+Function Invoke-UpdateCatalogSearch {
+ <#
+ .SYNOPSIS
+ Searches the Microsoft Update Catalog for the specific KB number.
+ #>
+ [OutputType([Microsoft.PowerShell.Commands.WebResponseObject])]
+ [CmdletBinding(SupportsShouldProcess = $False)]
+ Param(
+ [Parameter(Mandatory = $True)]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $UpdateId
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ If ($Null -ne $resourceStrings) {
+ try {
+ $params = @{
+ Uri = "$($resourceStrings.CatalogUris.Search)$($UpdateId)"
+ ContentType = $resourceStrings.ContentType.html
+ UserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
+ UseBasicParsing = $True
+ ErrorAction = $resourceStrings.Preferences.ErrorAction
+ }
+ Write-Verbose -Message "$($MyInvocation.MyCommand): search Catalog for [$UpdateId)]"
+ $searchResult = Invoke-WebRequest @params
+ }
+ catch [System.Net.WebException] {
+ Write-Warning -Message ($($MyInvocation.MyCommand))
+ Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message))
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to search the catalog: $Uri."
+ Throw $_.Exception.Message
+ }
+
+ If ($searchResult.StatusCode -eq "200") {
+ Write-Output -InputObject $searchResult
+ }
+ Else {
+ Write-Warning -Message "$($MyInvocation.MyCommand): no valid response."
+ }
+ }
+ Else {
+ Write-Warning -Message "$($MyInvocation.MyCommand): unable to retreive Update Catalog search URI."
+ }
+}
diff --git a/LatestUpdate/Private/New-MdtDrive.ps1 b/LatestUpdate/Private/New-MdtDrive.ps1
deleted file mode 100644
index 8a1a56c..0000000
--- a/LatestUpdate/Private/New-MdtDrive.ps1
+++ /dev/null
@@ -1,42 +0,0 @@
-Function New-MdtDrive {
- <#
- .SYNOPSIS
- Creates a new persistent PS drive mapped to an MDT share.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Path
- A path to a Microsoft Deployment Toolkit share.
-
- .PARAMETER Drive
- A PS drive letter to map to the MDT share.
- #>
- [CmdletBinding(SupportsShouldProcess = $True)]
- [OutputType([String])]
- Param (
- [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String]$Path,
-
- [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String]$Drive = "DS009"
- )
- $description = "MDT drive created by $($MyInvocation.MyCommand)"
- If ($mdtDrives = Get-MdtPersistentDrive | Where-Object { ($_.Path -eq $Path) -and ($_.Description -eq $Description) }) {
- Write-Verbose "Found MDT drive: $($mdtDrives[0].Name)"
- $output = $mdtDrives[0].Name
- }
- Else {
- If ($pscmdlet.ShouldProcess("$($Drive): to $($Path)", "Mapping")) {
- New-PSDrive -Name $Drive -PSProvider "MDTProvider" -Root $Path `
- -NetworkPath $Path -Description $description | Add-MDTPersistentDrive
- $psDrive = Get-MdtPersistentDrive | Where-Object { ($_.Path -eq $Path) -and ($_.Name -eq $Drive) }
- Write-Verbose "Found: $($psDrive.Name)"
- $output = $psDrive.Name
- }
- }
- Write-Output $output
-}
diff --git a/LatestUpdate/Private/New-MdtPackagesFolder.ps1 b/LatestUpdate/Private/New-MdtPackagesFolder.ps1
deleted file mode 100644
index 81a6920..0000000
--- a/LatestUpdate/Private/New-MdtPackagesFolder.ps1
+++ /dev/null
@@ -1,57 +0,0 @@
-Function New-MdtPackagesFolder {
- <#
- .SYNOPSIS
- Creates a folder in the MDT Packages node.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Drive
- An existing PS drive letter mapped to an MDT deployment share.
-
- .PARAMETER Path
- A new folder to create below the Packages node in the MDT deployment share.
- #>
- [CmdletBinding(SupportsShouldProcess = $True)]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String] $Drive,
-
- [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String] $Path
- )
-
- # Test path and create if it does not already exist
- If ((Test-Path -Path "$($Drive):\Packages\$Path" -Type 'Container')) {
- Write-Verbose "Path exists: $($Drive):\Packages\$Path"
- Write-Output $True
- }
- Else {
- # If path with multiple folders specified, create each one
- $folders = $Path -split "\\"
- $parent = "$($Drive):\Packages"
-
- # Walkthrough each folder in the path to create
- ForEach ($folder in $folders) {
- If (!(Test-Path -Path "$parent\$folder" -Type 'Container')) {
- If ($pscmdlet.ShouldProcess("$parent\$folder", "Creating")) {
- Try {
- New-Item -Path $parent -Enable "True" -Name $folder `
- -Comments "Created by 'New-MdtPackagesFolder'" `
- -ItemType "Folder"
- }
- Catch {
- Throw "Failed to create Packages folder."
- }
- }
- }
- $parent = "$parent\$folder"
-
- # Return status from New-Item
- Write-Output $?
- }
- }
-}
diff --git a/LatestUpdate/Private/Remove-MdtDrive.ps1 b/LatestUpdate/Private/Remove-MdtDrive.ps1
deleted file mode 100644
index 1d3b202..0000000
--- a/LatestUpdate/Private/Remove-MdtDrive.ps1
+++ /dev/null
@@ -1,23 +0,0 @@
-Function Remove-MdtDrive {
- <#
- .SYNOPSIS
- Removes a persistent PS drive mapped to an MDT share.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Drive
- A PS drive mapped to an MDT deployment share to remove.
- #>
- [CmdletBinding(SupportsShouldProcess = $True)]
- Param (
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String] $Drive
- )
- If ($pscmdlet.ShouldProcess("MDT drive: $($Drive)", "Removing")) {
- $MdtDrive = Get-MDTPersistentDrive | Where-Object { $_.Name -eq $Drive }
- Remove-MDTPersistentDrive -Name $MdtDrive.Name
- }
-}
diff --git a/LatestUpdate/Private/Remove-MdtPackage.ps1 b/LatestUpdate/Private/Remove-MdtPackage.ps1
deleted file mode 100644
index 29c9409..0000000
--- a/LatestUpdate/Private/Remove-MdtPackage.ps1
+++ /dev/null
@@ -1,45 +0,0 @@
-Function Remove-MdtPackage {
- <#
- .SYNOPSIS
- Removes all packages from a specified path.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Path
- Target path that the packages will be removed from.
- #>
- [CmdletBinding(SupportsShouldProcess = $True)]
- Param (
- [Parameter(Mandatory = $True, Position = 1, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [String] $Path
- )
-
- # Change to the target path
- Push-Location $Path
-
- # If change path is successful
- If ($?) {
- # Get packages from the current folder
- $packages = Get-ChildItem | Where-Object { $_.Name -like "Package*" }
-
- # Step through each package and remove it
- ForEach ($package in $packages) {
- If ($pscmdlet.ShouldProcess($package.Name, "Remove package")) {
- Try {
- # Remove, but don't force in case the update exists in another folder
- Write-Verbose -Message "Removing package $($package.Name)"
- Remove-Item -Path ".\$($package.Name)"
- }
- Catch {
- Write-Error "Failed to remove item $($package.Name)"
- }
- }
- }
-
- # Change back to the original location
- Pop-Location
- }
-}
diff --git a/LatestUpdate/Private/Select-LatestUpdate.ps1 b/LatestUpdate/Private/Select-LatestUpdate.ps1
deleted file mode 100644
index 3931837..0000000
--- a/LatestUpdate/Private/Select-LatestUpdate.ps1
+++ /dev/null
@@ -1,43 +0,0 @@
-Function Select-LatestUpdate {
- <#
- .SYNOPSIS
- Selects the latest update build number.
-
- .DESCRIPTION
- Selects the latest update build number from the update JSON at https://support.microsoft.com/app/content/api/content/asset/en-us/4000816.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- Original script: Copyright Keith Garner, All rights reserved.
- Forked from: https://gist.github.com/keithga/1ad0abd1f7ba6e2f8aff63d94ab03048
-
- .PARAMETER Updates
- An array of updates retrieved by Get-LatestUpdate.
- #>
- [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", `
- Justification = "MaxObj is false positive.")]
- [CmdletBinding(SupportsShouldProcess = $False)]
- [OutputType([String])]
- Param(
- [parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
- [ValidateNotNullOrEmpty()]
- [Array] $Updates
- )
- Begin {
- $maxObj = $Null
- $maxValue = [version]::new("0.0")
- }
- Process {
- ForEach ($update in $Updates) {
- Select-String -InputObject $Update -AllMatches -Pattern "(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)" |
- ForEach-Object { $_.matches.value } |
- ForEach-Object { $_ -as [version] } |
- ForEach-Object { If ( $_ -gt $maxValue ) { $maxObj += $Update; $maxValue = $_ } }
- }
- }
- End {
- Write-Output $maxObj
- }
-}
diff --git a/LatestUpdate/Private/Select-UniqueUrl.ps1 b/LatestUpdate/Private/Select-UniqueUrl.ps1
deleted file mode 100644
index edd056a..0000000
--- a/LatestUpdate/Private/Select-UniqueUrl.ps1
+++ /dev/null
@@ -1,35 +0,0 @@
-Function Select-UniqueUrl {
- <#
- .SYNOPSIS
- Selects unique URLs from the array of updates returned from Get-LatestUpdate.
-
- .DESCRIPTION
- Selects unique URLs from the array of updates returned from Get-LatestUpdate.
- Multiple updates can be release which could point to the same URL. To ensure we only download the update once,
- the a unique URL needs to be passed back to Save-LatestUpdate.
-
- .PARAMETER Updates
- An array of updates retrieved by Get-LatestUpdate.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Updates
- The array of latest cumulative updates retreived by Get-LatestUpdate.
- #>
- [CmdletBinding(SupportsShouldProcess = $False)]
- [OutputType([Array])]
- Param(
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, `
- HelpMessage = "The array of updates from Get-LatestUpdate.")]
- [ValidateNotNullOrEmpty()]
- [Array] $Updates
- )
- $urls = @()
- ForEach ( $update in $Updates ) {
- $urls += $update.Url
- }
- $urls = $urls | Select-Object -Unique
- $urls
-}
diff --git a/LatestUpdate/Private/Test-PSCore.ps1 b/LatestUpdate/Private/Test-PSCore.ps1
index a7cab62..7ccc3cd 100644
--- a/LatestUpdate/Private/Test-PSCore.ps1
+++ b/LatestUpdate/Private/Test-PSCore.ps1
@@ -1,28 +1,21 @@
Function Test-PSCore {
<#
- .SYNOPSIS
- Returns True is running on PowerShell Core.
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .PARAMETER Version
- The version of PowerShell Core. Optionally specified where value needs to be something other than 6.0.0.
+ .SYNOPSIS
+ Returns True if running on PowerShell Core.
#>
- [CmdletBinding()]
+ [CmdletBinding(SupportsShouldProcess = $False)]
[OutputType([Boolean])]
Param (
- [Parameter(ValueFromPipeline)]
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
- [String] $Version = '6.0.0'
+ [System.String] $Version = '6.0.0'
)
# Check whether current PowerShell environment matches or is higher than $Version
- If (($PSVersionTable.PSVersion -ge [version]::Parse($Version)) -and ($PSVersionTable.PSEdition -eq "Core")) {
- Write-Output $True
+ If (($PSVersionTable.PSVersion -ge [Version]::Parse($Version)) -and ($PSVersionTable.PSEdition -eq "Core")) {
+ Write-Output -InputObject $True
}
Else {
- Write-Output $False
+ Write-Output -InputObject $False
}
}
diff --git a/LatestUpdate/Public/Get-LatestAdobeFlashUpdate.ps1 b/LatestUpdate/Public/Get-LatestAdobeFlashUpdate.ps1
new file mode 100644
index 0000000..9dce82c
--- /dev/null
+++ b/LatestUpdate/Public/Get-LatestAdobeFlashUpdate.ps1
@@ -0,0 +1,55 @@
+Function Get-LatestAdobeFlashUpdate {
+ <#
+ .SYNOPSIS
+ Retrieves the latest Windows 10 Adobe Flash Player Update.
+
+ .DESCRIPTION
+ Retrieves the latest Windows 10 Adobe Flash Player Update from the Windows 10 update history feed.
+
+ .EXAMPLE
+
+ PS C:\> Get-LatestAdobeFlashUpdate
+
+ This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Adobe Flash Player Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/get-flash")]
+ [Alias("Get-LatestFlash")]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline, HelpMessage = "Windows 10 Semi-annual Channel version number.")]
+ [ValidateSet('1903', '1809', '1803', '1709', '1703', '1607')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Version = "1903"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # If resource strings are returned we can continue
+ If ($Null -ne $resourceStrings) {
+ ForEach ($ver in $Version) {
+
+ # Get the update feed and continue if successfully read
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows10
+ If ($Null -ne $updateFeed) {
+
+ # Filter the feed for Adobe Flash updates and continue if we get updates
+ $updateList = Get-UpdateAdobeFlash -UpdateFeed $updateFeed
+ If ($Null -ne $updateList) {
+
+ # Get download info for each update from the catalog
+ $downloadInfo = Get-UpdateCatalogDownloadInfo -UpdateId $updateList.ID
+
+ # Add the Version and Architecture properties to the list
+ $updateListWithVersion = Add-Property -InputObject $downloadInfo -Property "Note" -NewPropertyName "Version" `
+ -MatchPattern $resourceStrings.Matches.Windows10Version
+ $updateListWithArch = Add-Property -InputObject $updateListWithVersion -Property "Note" -NewPropertyName "Architecture" `
+ -MatchPattern $resourceStrings.Matches.Architecture
+
+ # Return object to the pipeline
+ Write-Output -InputObject $updateListWithArch
+ }
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Public/Get-LatestCumulativeUpdate.ps1 b/LatestUpdate/Public/Get-LatestCumulativeUpdate.ps1
new file mode 100644
index 0000000..7d1b638
--- /dev/null
+++ b/LatestUpdate/Public/Get-LatestCumulativeUpdate.ps1
@@ -0,0 +1,57 @@
+Function Get-LatestCumulativeUpdate {
+ <#
+ .SYNOPSIS
+ Retrieves the latest Windows 10 Cumulative Update.
+
+ .DESCRIPTION
+ Retrieves the latest Windows 10 Cumulative Update from the Windows 10 update history feed.
+
+ More information on Windows 10 Cumulative Updates can be found here: https://docs.microsoft.com/en-us/windows/deployment/update/
+
+ .EXAMPLE
+
+ PS C:\> Get-LatestCumulativeUpdate
+
+ This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Cumulative Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/get-latest")]
+ [Alias("Get-LatestUpdate")]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline, HelpMessage = "Windows 10 Semi-annual Channel version number.")]
+ [ValidateSet('1903', '1809', '1803', '1709', '1703', '1607')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Version = "1903"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # If resource strings are returned we can continue
+ If ($Null -ne $resourceStrings) {
+ ForEach ($ver in $Version) {
+
+ # Get the update feed and continue if successfully read
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows10
+ If ($Null -ne $updateFeed) {
+
+ # Filter the feed for cumulative updates and continue if we get updates
+ $updateList = Get-UpdateCumulative -UpdateFeed $updateFeed -Build $resourceStrings.VersionTable.Windows10[$ver]
+ If ($Null -ne $updateList) {
+
+ # Get download info for each update from the catalog
+ $downloadInfo = Get-UpdateCatalogDownloadInfo -UpdateId $updateList.ID
+
+ # Add the Version and Architecture properties to the list
+ $updateListWithVersion = Add-Property -InputObject $downloadInfo -Property "Note" -NewPropertyName "Version" `
+ -MatchPattern $resourceStrings.Matches.Windows10Version
+ $updateListWithArch = Add-Property -InputObject $updateListWithVersion -Property "Note" -NewPropertyName "Architecture" `
+ -MatchPattern $resourceStrings.Matches.Architecture
+
+ # Return object to the pipeline
+ Write-Output -InputObject $updateListWithArch
+ }
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Public/Get-LatestFlash.ps1 b/LatestUpdate/Public/Get-LatestFlash.ps1
deleted file mode 100644
index f972e56..0000000
--- a/LatestUpdate/Public/Get-LatestFlash.ps1
+++ /dev/null
@@ -1,85 +0,0 @@
-Function Get-LatestFlash {
- <#
- .SYNOPSIS
- Get the latest Adobe Flash Player update for Windows.
-
- .DESCRIPTION
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .LINK
- https://docs.stealthpuppy.com/latestupdate
-
- .EXAMPLE
- Get-LatestFlash
-
- Description:
- Enumerate the latest Adobe Flash Player update for the support versions of Windows.
- #>
- [CmdletBinding(SupportsShouldProcess = $False)]
- Param(
- [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Windows OS to search")]
- [ValidateNotNullOrEmpty()]
- [String] $OS
- )
-
- Begin {
- [String] $Feed = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/6ae59d69-36fc-8e4d-23dd-631d98bf74a9/atom'
- [regex] $Flash = ".*Adobe Flash Player.*"
- }
-
- Process {
-
- try {
- # Return the update feed
- $xml = Get-UpdateFeed -UpdateFeed $Feed
- }
- catch {
- Throw "Failed to return the update feed. Confirm feed is OK: $Feed"
- Break
- }
-
- # Find the most current date for the update
- [regex] $rxM = "(\d{4}-\d{2}-\d{2})"
- $date = $xml.feed.entry | Where-Object { $_.title -match $Flash } | Select-Object -ExpandProperty updated | `
- ForEach-Object { Get-RxString -String $_ -RegEx $rxM } | Sort-Object | Select-Object -Last 1
-
- # Return the KB published for that most current date
- $kbID = $xml.feed.entry | Where-Object { ($_.title -match $Flash) -and ($_.updated -match $date) } | Select-Object -ExpandProperty id `
- | ForEach-Object { $_.split(':') | Select-Object -Last 1 }
-
- If (($Null -eq $date) -or ($Null -eq $kbID)) {
- Write-Warning -Message "Failed to return usable Windows update content from the Microsoft feed."
- Write-Warning -Message "Microsoft appears to be returning different content for each request."
- Write-Warning -Message "Please check the feed content and try again later."
- Write-Warning -Message "Feed URI: $Feed"
- Break
- }
- Else {
- # Get the download link from Windows Update
- [String] $SearchString = "KB" + $kbID
-
- if ($OS) {
- $SearchString = $SearchString + " $OS"
- }
-
- $kbObj = Get-UpdateCatalogLink -SearchString $SearchString
- If ($Null -ne $kbObj) {
- # Contruct a table with KB, Id and Update description
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB $kbID
-
- # Process the IdTable to get a new array with KB, Architecture, Note and URL for each download
- $downloadArray = Get-UpdateDownloadArray -IdTable $idTable
- }
- }
- }
-
- End {
- # Write the list of updates to the pipeline
- If ($Null -ne $downloadArray) {
- Write-Output ($downloadArray | Sort-Object -Property Version -Descending)
- }
- }
-}
diff --git a/LatestUpdate/Public/Get-LatestMonthlyRollup.ps1 b/LatestUpdate/Public/Get-LatestMonthlyRollup.ps1
new file mode 100644
index 0000000..b6f26ac
--- /dev/null
+++ b/LatestUpdate/Public/Get-LatestMonthlyRollup.ps1
@@ -0,0 +1,60 @@
+Function Get-LatestMonthlyRollup {
+ <#
+ .SYNOPSIS
+ Retrieves the latest Windows 8.1 and 7 Monthly Rollup Update.
+
+ .DESCRIPTION
+ Retrieves the latest Windows 8.1 and 7 Monthly Rollup Update from the Windows 8.1/7 update history feed.
+
+ .EXAMPLE
+
+ PS C:\> Get-LatestMonthlyRollup
+
+ This commands reads the the Windows 8.1 update history feed and returns an object that lists the most recent Windows 8.1 Monthly Rollup Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/get-latest")]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline, HelpMessage = "Windows version.")]
+ [ValidateSet('Windows 8', 'Windows 7')]
+ [ValidateNotNullOrEmpty()]
+ [System.String] $Version = "Windows 8"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # If resource strings are returned we can continue
+ If ($Null -ne $resourceStrings) {
+ Switch ($Version) {
+ "Windows 8" {
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows8
+ $osName = "Windows 8.1|Windows Server"
+ }
+ "Windows 7" {
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows7
+ $osName = "Windows 7|Windows Server"
+ }
+ }
+ If ($Null -ne $updateFeed) {
+
+ # Filter the feed for monthly rollup updates and continue if we get updates
+ $updateList = Get-UpdateMonthly -UpdateFeed $updateFeed
+ If ($Null -ne $updateList) {
+
+ # Get download info for each update from the catalog
+ $downloadInfo = Get-UpdateCatalogDownloadInfo -UpdateId $updateList.ID -OS $osName -Architecture 'x86|x64'
+ $filteredDownloadInfo = $downloadInfo | Sort-Object -Unique -Property Note
+
+ # Add the Version and Architecture properties to the list
+ $updateListWithVersion = Add-Property -InputObject $filteredDownloadInfo -Property "Note" -NewPropertyName "Version" `
+ -MatchPattern $resourceStrings.Matches.Windows10Version
+ $updateListWithArch = Add-Property -InputObject $updateListWithVersion -Property "Note" -NewPropertyName "Architecture" `
+ -MatchPattern $resourceStrings.Matches.Architecture
+
+ # Return object to the pipeline
+ Write-Output -InputObject $updateListWithArch
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Public/Get-LatestNETFramework.ps1 b/LatestUpdate/Public/Get-LatestNETFramework.ps1
deleted file mode 100644
index d193190..0000000
--- a/LatestUpdate/Public/Get-LatestNETFramework.ps1
+++ /dev/null
@@ -1,57 +0,0 @@
-Function Get-LatestNetFramework {
- <#
- .SYNOPSIS
- Get the latest .NET Framework Cumulative update for Windows.
-
- .DESCRIPTION
-
- .NOTES
- Author: Gianni Fontanini
-
- .LINK
- https://docs.stealthpuppy.com/docs/latestupdate
-
- .EXAMPLE
- Get-LatestNETFramework -OS "Windows Server 2019"
-
- Description:
- Gets the latest .NET Framework Cumulative update for the given OS.
- #>
- [CmdletBinding(SupportsShouldProcess = $False)]
- Param(
- [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Windows OS to search")]
- [ValidateNotNullOrEmpty()]
- [string] $OS = "Windows 10"
- )
-
- Process {
- [regex] $rx = "]*>([^<]+)<\/a>"
-
- # $kbObj = Get-UpdateCatalogLink -SearchString "Cumulative Update for .NET Framework $OS"
- $kbObj = Get-UpdateCatalogLink -SearchString "Cumulative Update for .NET Framework"
-
- # Find the latest KB ID
- $LastObject = ((($kbObj.Links | Where-Object ID -match '_link').outerHTML -replace $rx, '$1').TrimStart()).TrimEnd() `
- | Sort-Object | Select-Object -last 1
- # Regex the KB from the string
- $kbID = [regex]::match($LastObject,"KB\d*").Groups[0].Value
-
- If ($Null -ne $kbObj) {
- # Contruct a table with KB, Id and Update description
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB $kbID
-
- # Process the IdTable to get a new array with KB, Architecture, Note and URL for each download
- $downloadArray = Get-UpdateDownloadArray -IdTable $idTable
-
- $downloadArray = $downloadArray | Sort-Object -Property Note | Select-Object -Last 1
- }
-
- }
-
- End {
- # Write the list of updates to the pipeline
- If ($Null -ne $downloadArray) {
- Write-Output ($downloadArray | Sort-Object -Property Note)
- }
- }
-}
diff --git a/LatestUpdate/Public/Get-LatestNetFrameworkUpdate.ps1 b/LatestUpdate/Public/Get-LatestNetFrameworkUpdate.ps1
new file mode 100644
index 0000000..00affd1
--- /dev/null
+++ b/LatestUpdate/Public/Get-LatestNetFrameworkUpdate.ps1
@@ -0,0 +1,64 @@
+Function Get-LatestNetFrameworkUpdate {
+ <#
+ .SYNOPSIS
+ Retrieves the latest Windows 10 .NET Framework Cumulative Update.
+
+ .DESCRIPTION
+ Retrieves the latest Windows 10 .NET Framework Cumulative Update from the Windows 10 update history feed.
+
+ .EXAMPLE
+
+ PS C:\> Get-LatestNetFrameworkUpdate
+
+ This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 .NET Framework Cumulative Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/get-latest")]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline, HelpMessage = "Windows 10 Semi-annual Channel version number.")]
+ [ValidateSet('1903', '1809', '1803', '1709', '1703', '1607')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Version = "1903"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # If resource strings are returned we can continue
+ If ($Null -ne $resourceStrings) {
+
+ # Get the update feed and continue if successfully read
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.NetFramework
+ If ($Null -ne $updateFeed) {
+
+ # Filter the feed for NET Framework updates and continue if we get updates
+ $updateList = Get-UpdateNetFramework -UpdateFeed $updateFeed
+ If ($Null -ne $updateList) {
+ ForEach ($update in $updateList) {
+
+ # Get download info for each update from the catalog
+ $downloadInfo = Get-UpdateCatalogDownloadInfo -UpdateId $update.ID -OS $resourceStrings.SearchStrings.NetFrameworkWindows10 -Architecture ""
+ $filteredDownloadInfo = $downloadInfo | Sort-Object -Unique -Property Note
+
+ # Add the Version and Architecture properties to the list
+ $updateListWithVersion = Add-Property -InputObject $filteredDownloadInfo -Property "Note" -NewPropertyName "Version" `
+ -MatchPattern $resourceStrings.Matches.Windows10Version
+ $updateListWithArch = Add-Property -InputObject $updateListWithVersion -Property "Note" -NewPropertyName "Architecture" `
+ -MatchPattern $resourceStrings.Matches.Architecture
+
+ # If the value for Architecture is blank, make it "x86"
+ $i = 0
+ ForEach ($update in $updateListWithArch) {
+ If ($update.Architecture.Length -eq 0) {
+ $updateListWithArch[$i].Architecture = "x86"
+ }
+ $i++
+ }
+
+ # Return object to the pipeline
+ Write-Output -InputObject $updateListWithArch
+ }
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Public/Get-LatestServicingStack.ps1 b/LatestUpdate/Public/Get-LatestServicingStack.ps1
deleted file mode 100644
index 74662ea..0000000
--- a/LatestUpdate/Public/Get-LatestServicingStack.ps1
+++ /dev/null
@@ -1,135 +0,0 @@
-Function Get-LatestServicingStack {
- <#
- .SYNOPSIS
- Get the latest Servicing Stack Update for Windows 10.
-
- .DESCRIPTION
- Returns the latest Servicing Stack Update for Windows 10 and corresponding Windows Server from the Microsoft Update Catalog by querying the Update History page.
-
- Get-LatestUpdate outputs the result as a table that can be passed to Save-LatestUpdate to download the update locally. Then do one or more of the following:
- - Import the update into an MDT share with Import-LatestUpdate to speed up deployment of Windows (reference images etc.)
- - Apply the update to an offline WIM using DISM
- - Deploy the update with ConfigMgr (if not using WSUS)
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
- Latest Servicing Stack Updates: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV990001
-
- .LINK
- https://docs.stealthpuppy.com/latestupdate
-
- .PARAMETER Version
- Windows 10 version to return the Servicing Stack Update for. Use the Year Month notation for Windows 10 versions. Supports 1607+.
-
- .EXAMPLE
- Get-LatestServicingStack
-
- Description:
- Get the latest Servicing Stack Update for all supported Windows 10 and Windows Server versions.
- #>
- [CmdletBinding(SupportsShouldProcess = $False)]
- Param(
- [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Windows 10 version to search")]
- [ValidateSet('1607', '1703', '1709', '1803', '1809', '1903')]
- [ValidateNotNullOrEmpty()]
- [String[]] $Version = @('1607', '1703', '1709', '1803', '1809', '1903')
- )
-
- Begin {
- # Update RSS feed and search string
- [string] $Feed = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/6ae59d69-36fc-8e4d-23dd-631d98bf74a9/atom'
- [regex] $searchString = "Servicing stack update.*"
-
- # Return the XML from the feed and filter for the Servicing Stack Updates
- try {
- # Return the update feed
- $xml = Get-UpdateFeed -UpdateFeed $Feed
- }
- catch {
- Throw "Failed to return the update feed. Confirm feed is OK: $Feed"
- Break
- }
- $servicingStacks = $xml.feed.entry | Where-Object { $_.title -match $searchString } | Select-Object title, id, updated
-
- # RegEx for month; Output array
- [regex] $rxM = "(\d{4}-\d{2}-\d{2})"
- $downloadArray = @()
- }
-
- Process {
- # Step through the servicing stack feed to find the latest KB article for each Windows 10 version
- ForEach ($ver in $Version) {
-
- # Find the most current date for each entry for each Windows 10 version
- $date = $servicingStacks | Where-Object { $_.title -match $ver } | Sort-Object -Property id | `
- Select-Object -ExpandProperty updated | `
- ForEach-Object { ([regex]::match($_, $rxM).Groups[1].Value) } | Select-Object -Last 1
-
- # Return the KB published for that most current date
- If ($Null -ne $date) {
- $kbID = $servicingStacks | Where-Object { ($_.title -match $ver) -and ($_.updated -match $date) } | `
- Select-Object -ExpandProperty id | ForEach-Object { $_.split(':') | Select-Object -Last 1 }
- }
-
- # Multiple KBs could be returned, step through each
- ForEach ($id in $kbID) {
-
- # Read the for updates for that KB from the Microsoft Update Catalog
- Write-Verbose -Message "Getting update catalog links for KB :$id"
- $kbObj = Get-UpdateCatalogLink -KB $id
- If ($Null -ne $kbObj) {
-
- # Contruct a table with KB, Id and Update description
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB $id
-
- # Step through the ids for each update
- ForEach ($idItem in $idTable) {
- try {
- # Grab the URL for each update
- Write-Verbose -Message "Checking Microsoft Update Catalog for Id: $($idItem.id)."
- $post = @{ size = 0; updateID = $idItem.id; uidInfo = $idItem.id } | ConvertTo-Json -Compress
- $postBody = @{ updateIDs = "[$post]" }
- $url = Invoke-WebRequest -Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' `
- -Method Post -Body $postBody -UseBasicParsing -ErrorAction SilentlyContinue |
- Select-Object -ExpandProperty Content |
- Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" |
- ForEach-Object { $_.matches.value }
- }
- catch {
- Throw "Failed to parse Microsoft Update Catalog for Id: $($idItem.id)."
- Break
- }
- finally {
- # Build an array for each update and add it to the output array
- If ($url) {
- Write-Verbose -Message "Adding $url to output."
- $newItem = New-Object PSObject
- $newItem | Add-Member -type NoteProperty -Name 'KB' -Value $idItem.KB
- $newItem | Add-Member -type NoteProperty -Name 'Arch' `
- -Value (Get-RxString -String $idItem.Note -RegEx "\s+([a-zA-Z0-9]+)-based")
- $newItem | Add-Member -type NoteProperty -Name 'Version' -Value $ver
- $newItem | Add-Member -type NoteProperty -Name 'Note' -Value $idItem.Note
- $newItem | Add-Member -type NoteProperty -Name 'URL' -Value $url
- $downloadArray += $newItem
- }
- }
- }
- }
- Else {
- Write-Warning -Message "Failed to return usable Windows update content from the Microsoft feed."
- Write-Warning -Message "Microsoft appears to be returning different content for each request."
- Write-Warning -Message "Please check the feed content and try again later."
- Write-Warning -Message "Feed URI: $Feed"
- }
- }
- }
- }
-
- End {
- # Return the array of Servicing Stack Updates to the pipeline
- If ($Null -ne $downloadArray) {
- Write-Output ($downloadArray | Sort-Object -Property Version -Descending)
- }
- }
-}
diff --git a/LatestUpdate/Public/Get-LatestServicingStackUpdate.ps1 b/LatestUpdate/Public/Get-LatestServicingStackUpdate.ps1
new file mode 100644
index 0000000..8480b07
--- /dev/null
+++ b/LatestUpdate/Public/Get-LatestServicingStackUpdate.ps1
@@ -0,0 +1,57 @@
+Function Get-LatestServicingStackUpdate {
+ <#
+ .SYNOPSIS
+ Retrieves the latest Windows 10 Servicing Stack Update.
+
+ .DESCRIPTION
+ Retrieves the latest Windows 10 Servicing Stack Update from the Windows 10 update history feed.
+
+ More information on Windows 10 Servicing Stack Updates can be found here: https://docs.microsoft.com/en-us/windows/deployment/update/servicing-stack-updates
+
+ .EXAMPLE
+
+ PS C:\> Get-LatestServicingStackUpdate
+
+ This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Servicing Stack Update.
+ #>
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $False, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/get-stack")]
+ [Alias("Get-LatestServicingStack")]
+ Param (
+ [Parameter(Mandatory = $False, Position = 0, ValueFromPipeline, HelpMessage = "Windows 10 Semi-annual Channel version number.")]
+ [ValidateSet('1903', '1809', '1803', '1709', '1703', '1607')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Version = "1903"
+ )
+
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
+
+ # If resource strings are returned we can continue
+ If ($Null -ne $resourceStrings) {
+
+ # Get the update feed and continue if successfully read
+ ForEach ($ver in $Version) {
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows10
+ If ($Null -ne $updateFeed) {
+
+ # Filter the feed for servicing stack updates and continue if we get updates
+ $updateList = Get-UpdateServicingStack -UpdateFeed $updateFeed -Version $ver
+ If ($Null -ne $updateList) {
+
+ # Get download info for each update from the catalog
+ $downloadInfo = Get-UpdateCatalogDownloadInfo -UpdateId $updateList.ID
+
+ # Add the Version and Architecture properties to the list
+ $updateListWithVersion = Add-Property -InputObject $downloadInfo -Property "Note" -NewPropertyName "Version" `
+ -MatchPattern $resourceStrings.Matches.Windows10Version
+ $updateListWithArch = Add-Property -InputObject $updateListWithVersion -Property "Note" -NewPropertyName "Architecture" `
+ -MatchPattern $resourceStrings.Matches.Architecture
+
+ # Return object to the pipeline
+ Write-Output -InputObject $updateListWithArch
+ }
+ }
+ }
+ }
+}
diff --git a/LatestUpdate/Public/Get-LatestUpdate.ps1 b/LatestUpdate/Public/Get-LatestUpdate.ps1
deleted file mode 100644
index 6ccf975..0000000
--- a/LatestUpdate/Public/Get-LatestUpdate.ps1
+++ /dev/null
@@ -1,153 +0,0 @@
-Function Get-LatestUpdate {
- <#
- .SYNOPSIS
- Get the latest Cumulative or Monthly Rollup update for Windows.
-
- .DESCRIPTION
- Returns the latest Cumulative or Monthly Rollup updates for Windows 10 / 8.1 / 7 and corresponding Windows Server from the Microsoft Update Catalog by querying the Update History page.
-
- Get-LatestUpdate outputs the result as a table that can be passed to Save-LatestUpdate to download the update locally. Then do one or more of the following:
- - Import the update into an MDT share with Import-LatestUpdate to speed up deployment of Windows (reference images etc.)
- - Apply the update to an offline WIM using DISM
- - Deploy the update with ConfigMgr (if not using WSUS)
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- Original script: Copyright Keith Garner, All rights reserved.
- Forked from: https://gist.github.com/keithga/1ad0abd1f7ba6e2f8aff63d94ab03048
-
- .LINK
- https://docs.stealthpuppy.com/latestupdate
-
- .PARAMETER WindowsVersion
- Specifiy the Windows version to search for updates. Valid values are Windows10, Windows8, Windows7 (applies to desktop and server editions).
-
- .PARAMETER Build
- Dynamic parameter used with -WindowsVersion 'Windows10' Specify the Windows 10 build number for searching cumulative updates. Supports '17133', '16299', '15063', '14393', '10586', '10240'.
-
- .EXAMPLE
- Get-LatestUpdate
-
- Description:
- Get the latest Cumulative Update for Windows 10 Semi-Annual Channel.
-
- .EXAMPLE
- Get-LatestUpdate -WindowsVersion Windows10
-
- Description:
- Get the latest Cumulative Update for Windows 10 Semi-Annual Channel.
-
- .EXAMPLE
- Get-LatestUpdate -WindowsVersion Windows10 -Build 14393
-
- Description:
- Enumerate the latest Cumulative Update for Windows 10 1607 and Windows Server 2016.
-
- .EXAMPLE
- Get-LatestUpdate -WindowsVersion Windows10 -Build 15063
-
- Description:
- Enumerate the latest Cumulative Update for Windows 10 1703.
-
- .EXAMPLE
- Get-LatestUpdate -WindowsVersion Windows8
-
- Description:
- Enumerate the latest Monthly Update for Windows Server 2012 R2 / Windows 8.1.
-
- .EXAMPLE
- Get-LatestUpdate -WindowsVersion Windows7
-
- Description:
- Enumerate the latest Monthly Update for Windows 7 (and Windows 7 Embedded).
- #>
- [CmdletBinding(SupportsShouldProcess = $False)]
- Param(
- [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Select the OS to search for updates")]
- [ValidateSet('Windows10', 'Windows8', 'Windows7')]
- [ValidateNotNullOrEmpty()]
- [String] $WindowsVersion = "Windows10",
-
- [Parameter(Mandatory = $False, Position = 1, HelpMessage = "Provide a Windows 10 build number")]
- [ValidateSet('18362', '17763', '17134', '16299', '15063', '14393', '10240', '^(?!.*Preview)(?=.*Monthly).*')]
- [ValidateNotNullOrEmpty()]
- [String] $Build = "18362"
- )
- Begin {
- # Set values for -Build as required for each platform
- Switch ($WindowsVersion) {
- "Windows10" {
- [String] $Feed = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/6ae59d69-36fc-8e4d-23dd-631d98bf74a9/atom'
- If ($Build -eq "^(?!.*Preview)(?=.*Monthly).*") { $Build = "18362" }
- }
- "Windows8" {
- [String] $Feed = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/b905caa1-d413-c90c-bed3-20aead901092/atom'
- [String] $Build = "^(?!.*Preview)(?=.*Monthly).*"
- }
- "Windows7" {
- [String] $Feed = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/f825ca23-c7d1-aab8-4513-64980e1c3007/atom'
- [String] $Build = "^(?!.*Preview)(?=.*Monthly).*"
- }
- }
- Write-Verbose -Message "Checking updates for $WindowsVersion $Build."
- }
-
- Process {
- try {
- # Return the update feed
- $xml = Get-UpdateFeed -UpdateFeed $Feed
- }
- catch {
- Throw "Failed to return the update feed. Confirm feed is OK: $Feed"
- Break
- }
-
- Switch ($WindowsVersion) {
- "Windows10" {
- # Sort feed for titles that match Build number; Find the largest minor build number
- [regex] $rxB = "$Build.(\d+)"
- $buildMatches = $xml.feed.entry | Where-Object -Property title -match $Build
- Write-Verbose -Message "Found $($buildMatches.Count) items matching build $Build."
- $latestVersion = $buildMatches | ForEach-Object { ($rxB.match($_.title)).value.split('.') | Select-Object -Last 1 } `
- | ForEach-Object { [convert]::ToInt32($_, 10) } | Sort-Object -Descending | Select-Object -First 1
-
- # Re-match feed for major.minor number and return the KB number from the Id field
- Write-Verbose -Message "Latest Windows 10 build is: $Build.$latestVersion."
- $kbID = $xml.feed.entry | Where-Object -Property title -match "$Build.$latestVersion" | Select-Object -ExpandProperty id `
- | ForEach-Object { $_.split(':') | Select-Object -Last 1 }
- }
- default {
- $buildMatches = $xml.feed.entry | Where-Object -Property title -match $Build
- $kbID = $buildMatches | Select-Object -ExpandProperty ID | ForEach-Object { $_.split(':') | Select-Object -Last 1 } `
- | Sort-Object -Descending | Select-Object -First 1
- }
- }
-
- If (($Null -eq $buildMatches) -or ($Null -eq $latestVersion) -or ($Null -eq $kbID)) {
- Write-Warning -Message "Failed to return usable Windows update content from the Microsoft feed."
- Write-Warning -Message "Microsoft appears to be returning different content for each request."
- Write-Warning -Message "Please check the feed content and try again later."
- Write-Warning -Message "Feed URI: $Feed"
- Break
- }
- Else {
- # Get the download link from Windows Update
- $kbObj = Get-UpdateCatalogLink -KB $kbID
- If ($Null -ne $kbObj) {
- # Contruct a table with KB, Id and Update description
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB $kbID
-
- # Process the IdTable to get a new array with KB, Architecture, Note and URL for each download
- $downloadArray = Get-UpdateDownloadArray -IdTable $idTable
- }
- }
- }
- End {
- # Write the list of updates to the pipeline
- If ($Null -ne $downloadArray) {
- Write-Output ($downloadArray | Sort-Object -Property Version -Descending)
- }
- }
-}
diff --git a/LatestUpdate/Public/Import-LatestUpdate.ps1 b/LatestUpdate/Public/Import-LatestUpdate.ps1
deleted file mode 100644
index e2b5545..0000000
--- a/LatestUpdate/Public/Import-LatestUpdate.ps1
+++ /dev/null
@@ -1,123 +0,0 @@
-Function Import-LatestUpdate {
- <#
- .SYNOPSIS
- Imports the latest Windows packages into an MDT deployment share.
-
- .DESCRIPTION
- This function will import packages into an MDT Deployment Share. Retrieve the latest Cumulative updates for Windows 10 and Windows Server 2016 gathered by Get-LatestUpdate and downloaded with Save-LatestUpdate
-
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .LINK
- https://docs.stealthpuppy.com/latestupdate
-
- .PARAMETER UpdatePath
- The folder containing the updates to import into the MDT deployment share.
-
- .PARAMETER DeployRoot
- Specify the path to the MDT deployment share.
-
- .PARAMETER PackagePath
- A packges folder to import into relative to the Packages folder in the MDT share.
-
- .PARAMETER Clean
- Before importing the latest updates into the target path, remove any existing update package.
-
- .EXAMPLE
- Get-LatestUpdate | Save-LatestUpdate -Path "C:\Temp\Updates"
- Import-LatestUpdate -UpdatePath "C:\Temp\Updates" -DeployRoot "\\server\reference" -PackagePath "Windows 10"
-
- Description:
- Import the latest update gathered from Get-LatestUpdate into the deployment share \\server\reference under 'Packages\Windows 10'.
-
- .EXAMPLE
- Import-LatestUpdate -UpdatePath "C:\Temp\Updates" -DeployRoot "\\server\reference" -PackagePath "Windows 10" -Clean
-
- Description:
- Import the update stored in C:\Temp\Updates into the deployment share \\server\reference under 'Packages\Windows 10'. Any existing packages will be removed before the import.
- #>
- [CmdletBinding(SupportsShouldProcess = $True)]
- Param (
- [Parameter(Mandatory = $False, ValueFromPipeline = $True, `
- HelpMessage = "Specify the folder containing the MSU update/s to import.")]
- [ValidateScript( { If (Test-Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })]
- [ValidateNotNullOrEmpty()]
- [String] $UpdatePath = $PWD,
-
- [Parameter(Mandatory = $True, HelpMessage = "Specify an MDT deployment share to apply the update to.")]
- [ValidateScript( { If (Test-Path $_ -PathType 'Container') { $True } Else { Throw "Cannot find path $_" } })]
- [ValidateNotNullOrEmpty()]
- [String] $DeployRoot,
-
- [Parameter(Mandatory = $False, HelpMessage = "A sub-folder in the MDT Packages folder.")]
- [ValidateNotNullOrEmpty()]
- [String] $PackagePath,
-
- [Parameter(Mandatory = $False, `
- HelpMessage = "Remove the updates from the target MDT deployment share before importing the new updates.")]
- [Switch] $Clean
- )
- Begin {
- # If running on PowerShell Core, error and exit.
- If (Test-PSCore) {
- Write-Error -Message "PowerShell Core doesn't support PSSnapins. We can't load the MicrosoftDeploymentToolkit module." -ErrorAction Stop
- Break
- }
-
- If (Import-MdtModule) {
- If ($pscmdlet.ShouldProcess($Path, "Mapping")) {
- [String] $drive = "DS004"
- $drive = New-PSDrive -Name $drive -PSProvider MDTProvider -Root $DeployRoot
- }
- }
- Else {
- Write-Error -Message "Failed to import the MDT PowerShell module. Please install the MDT Workbench and try again." -ErrorAction Stop
- }
- # Ensure file system paths are valid and don't include trailing \
- $UpdatePath = Get-ValidPath $UpdatePath
- }
- Process {
- # If $PackagePath is specified, use a sub-folder of MDT Share\Packages
- If ($PSBoundParameters.ContainsKey('PackagePath')) {
- $dest = "$($drive):\Packages\$($PackagePath)"
- If ($pscmdlet.ShouldProcess($PackagePath, "New Package Folder")) {
- Try {
- New-MdtPackagesFolder -Drive $drive -Path $PackagePath
- }
- Catch {
- Write-Error -Message "Failed to create packages folder $($PackagePath)." -ErrorAction Stop
- }
- }
- }
- Else {
- # If no path specified, we'll import directly into the Packages folder
- $dest = "$($drive):\Packages"
- }
- Write-Verbose "Destination is $($dest)"
-
- # If -Clean is specified, remove packages from the destination folder
- If ($Clean) {
- Remove-MdtPackage -Path $dest
- }
-
- # Validate the provided local path and import the update package
- If ($UpdatePath -ne $False) {
- If ($pscmdlet.ShouldProcess("From $($UpdatePath) to $($dest)", "Importing")) {
- Try {
- Import-MdtPackage -Path $dest -SourcePath $UpdatePath -ErrorAction SilentlyContinue -ErrorVariable importError
- }
- Catch {
- Write-Error -Message "Failed to import the package."
- }
- }
- }
- Else {
- Write-Error -Message "Validation failed on the provided path $UpdatePath" -ErrorAction Stop
- }
- }
- End {
- If ($importError) { Write-Output $importError }
- }
-}
diff --git a/LatestUpdate/Public/Save-LatestUpdate.ps1 b/LatestUpdate/Public/Save-LatestUpdate.ps1
index 7341e56..3fa0d1d 100644
--- a/LatestUpdate/Public/Save-LatestUpdate.ps1
+++ b/LatestUpdate/Public/Save-LatestUpdate.ps1
@@ -1,171 +1,143 @@
Function Save-LatestUpdate {
<#
.SYNOPSIS
- Downloads the latest cumulative update passed from Get-LatestUpdate.
+ Downloads the latest Windows 10 Cumulative, Servicing Stack and Adobe Flash Player updates.
.DESCRIPTION
- Downloads the latest cumulative update passed from Get-LatestUpdate to a local folder.
+ Downloads the latest Windows 10 Cumulative, Servicing Stack and Adobe Flash Player updates to a local folder.
Then do one or more of the following:
- Import the update into an MDT share with Import-LatestUpdate to speed up deployment of Windows (reference images etc.)
- Apply the update to an offline WIM using DISM
- Deploy the update with ConfigMgr (if not using WSUS)
- .NOTES
- Author: Aaron Parker
- Twitter: @stealthpuppy
-
- .LINK
- https://docs.stealthpuppy.com/latestupdate
-
- .PARAMETER Updates
- The array of latest cumulative updates retreived by Get-LatestUpdate.
-
- .PARAMETER Path
- A destination path for downloading the cumulative updates to. This path must exist. Uses the current diretory by default.
-
- .PARAMETER ForceWebRequest
- Forces the use of Invoke-WebRequest over Start-BitsTransfer on Windows PowerShell.
-
.EXAMPLE
- Get-LatestUpdate | Save-LatestUpdate
- Description:
- Retreives the latest Windows 10 Cumulative Update with Get-LatestUpdate and passes the array of updates to Save-LatestUpdate on the pipeline.
- Save-LatestUpdate then downloads the latest updates to the current directory.
-
- .EXAMPLE
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build 14393
- Save-LatestUpdate -Updates $Updates -Path C:\Temp\Update
-
- Description:
- Retreives the latest Windows 10 build 14393 (1607) Cumulative Update with Get-LatestUpdate, saved to the variable $Updates.
- Save-LatestUpdate then downloads the latest updates to C:\Temp\Update.
-
- .EXAMPLE
- $Updates = Get-LatestUpdate
- Save-LatestUpdate -Updates $Updates -Path C:\Temp\Update -ForceWebRequest
+ PS C:\> Get-LatestServicingStackUpdate | Save-LatestUpdate
- Description:
- Retreives the latest Windows 10 build Cumulative Update with Get-LatestUpdate, saved to the variable $Updates.
- Save-LatestUpdate then downloads the latest updates to C:\Temp\Update using Invoke-WebRequest instead of Start-BitsTransfer.
+ This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Servicing Stack Updates. The output is then passed to Save-LatestUpdate and each update is downloaded locally.
#>
- [CmdletBinding(SupportsShouldProcess = $True)]
- [OutputType([Array])]
+ [OutputType([System.Management.Automation.PSObject])]
+ [CmdletBinding(SupportsShouldProcess = $True, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/download")]
Param(
- [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, `
- HelpMessage = "The array of updates from Get-LatestUpdate.")]
+ [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
- [Array] $Updates,
-
- [Parameter(Mandatory = $False, Position = 1, ValueFromPipeline = $False, `
- HelpMessage = "Specify a target path to download the update(s) to.")]
+ [System.Management.Automation.PSObject] $Updates,
+
+ [Parameter(Mandatory = $False, Position = 1)]
[ValidateNotNullOrEmpty()]
[ValidateScript( {
- If (!(Test-Path -Path $_ -PathType 'Container')) {
+ If (Test-Path -Path $_ -PathType 'Container') {
+ Return $True
+ }
+ Else {
Throw "Cannot find path $_"
}
- Return $True
})]
- [String] $Path = $PWD,
-
- [Parameter(Mandatory = $False)]
- [String] $ProxyURL,
+ [System.String] $Path = $PWD,
[Parameter(Mandatory = $False)]
- [pscredential] $ProxyCredentials,
+ [System.String] $Proxy,
[Parameter(Mandatory = $False)]
- [switch] $ForceWebRequest,
+ [System.Management.Automation.PSCredential]
+ $Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $False)]
- [switch] $Force
+ [System.Switch] $ForceWebRequest
)
- Begin {
- $Path = Get-ValidPath $Path
- $output = @()
- }
+ # Get module strings from the JSON
+ $resourceStrings = Get-ModuleResource
- Process {
+ # Output object
+ $updateList = New-Object -TypeName System.Collections.ArrayList
- # Step through each update in $Updates
- ForEach ($update in $Updates) {
+ # Step through each update in $Updates
+ ForEach ($update in $Updates) {
- # Create the target file path where the update will be saved
- $filename = Split-Path $update.URL -Leaf
- $target = Join-Path $Path $filename
- $output += $target
- Write-Verbose "Download target will be $target"
+ # Create the target file path where the update will be saved
+ $filename = Split-Path -Path $update.URL -Leaf
+ $target = Join-Path -Path $Path -ChildPath $filename
- # If the update is not already downloaded, download it.
- If (!(Test-Path -Path $target) -or $Force.IsPresent) {
- If ($ForceWebRequest -or (Test-PSCore)) {
-
- # Running on PowerShell Core or ForceWebRequest
- If ($pscmdlet.ShouldProcess($update.URL, "WebDownload")) {
- try {
- $WebRequestParams = @{
- Uri = $update.URL
- OutFile = $target
- UseBasicParsing = $true
- ErrorAction = "Stop"
- }
-
- if ($ProxyUrl) {
- $WebRequestParams.Proxy = $ProxyUrl
- }
-
- if ($ProxyCredentials) {
- $WebRequestParams.ProxyCredentials = $ProxyCredentials
- }
-
- Invoke-WebRequest @WebRequestParams
+ # If the update is not already downloaded, download it.
+ If (Test-Path -Path $target) {
+ Write-Verbose -Message "File exists: $target. Skipping download."
+ }
+ Else {
+ If ($ForceWebRequest -or (Test-PSCore)) {
+ If ($pscmdlet.ShouldProcess($update.URL, "WebDownload")) {
+ #Running on PowerShell Core or ForceWebRequest
+ try {
+ $params = @{
+ Uri = $update.URL
+ OutFile = $target
+ UseBasicParsing = $True
+ ErrorAction = $resourceStrings.Preferences.ErrorAction
+ }
+ If ($PSBoundParameters.ContainsKey($Proxy)) {
+ $params.Proxy = $Proxy
}
- catch {
- Throw $_
+ If ($PSBoundParameters.ContainsKey($Credential)) {
+ $params.ProxyCredentials = $Credential
}
+ $result = Invoke-WebRequest @params
+ }
+ catch [System.Net.WebException] {
+ Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message))
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to download: $($update.URL)."
+ Throw $_.Exception.Message
}
}
- Else {
-
- # Running on Windows PowerShell
- If ($pscmdlet.ShouldProcess($(Split-Path $update.URL -Leaf), "BitsDownload")) {
- try {
- $BitsParams = @{
- Source = $update.URL
- Destination = $target
- Priority = "High"
- DisplayName = $update.Note
- Description = "Downloading $($update.URL)"
- ErrorAction = "Stop"
- }
-
- if ($ProxyURL) {
- # Set priority to Foreground because the proxy will remove the Range protocol header
- $BitsParams.Priority = "Foreground"
- $BitsParams.ProxyUsage = "Override"
- $BitsParams.ProxyList = $ProxyURL
- }
-
- if ($ProxyCredentials) {
- $BitsParams.ProxyCredential = $ProxyCredentials
- }
-
- Start-BitsTransfer @BitsParams
+ }
+ Else {
+ If ($pscmdlet.ShouldProcess($(Split-Path $update.URL -Leaf), "BitsDownload")) {
+ #Running on Windows PowerShell
+ try {
+ $params = @{
+ Source = $update.URL
+ Destination = $target
+ Priority = "High"
+ DisplayName = $update.Note
+ Description = "Downloading $($update.URL)"
+ ErrorAction = $resourceStrings.Preferences.ErrorAction
+ }
+ If ($PSBoundParameters.ContainsKey($Proxy)) {
+ # Set priority to Foreground because the proxy will remove the Range protocol header
+ $params.Priority = "Foreground"
+ $params.ProxyUsage = "Override"
+ $params.ProxyList = $Proxy
}
- catch {
- Throw $_
+ If ($PSBoundParameters.ContainsKey($Credential)) {
+ $params.ProxyCredential = $ProxyCredentials
}
+ $result = Start-BitsTransfer @params
+ }
+ catch [System.Net.WebException] {
+ Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message))
+ }
+ catch [System.Exception] {
+ Write-Warning -Message "$($MyInvocation.MyCommand): failed to download: $($update.URL)."
+ Throw $_.Exception.Message
}
}
}
+ If ($result.StatusCode -eq "200") {
+ $PSObject = [PSCustomObject] @{
+ Note = $update.Note
+ ID = $update.KB
+ Target = $target
+ }
+ $updateList.Add($PSObject) | Out-Null
+ }
Else {
- Write-Verbose "File exists: $target. Skipping download."
+ Write-Warning -Message "$($MyInvocation.MyCommand): no valid response."
}
}
}
- End {
- Write-Output $output
- }
+
+ # Output results to the pipeline
+ Write-Output -InputObject $updateList
}
diff --git a/README.md b/README.md
index a65aa57..1f77b83 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
## About
-This repository hosts a module for retrieving the latest Cumulative Update, Monthly Rollups, Servicing Stack and Adobe Flash Player updates for various Windows builds from the Microsoft Update Catalog. In addition, it provides functions for downloading the update files and importing them into a Microsoft Deployment Toolkit deployment share for speeding the creation of reference images or Windows deployments.
+This repository hosts a module for retrieving and downloading the latest Windows 10 Cumulative Update, Servicing Stack and Adobe Flash Player updates and Windows 8.1 / 7 Monthly Rollups from the Microsoft Update Catalog. The updates can then be imported into a Microsoft Deployment Toolkit deployment share for speeding the creation of reference images or Windows deployments.
Importing a cumulative update into [the Packages nodes in an MDT share](https://docs.microsoft.com/en-us/sccm/mdt/use-the-mdt#ConfiguringPackagesintheDeploymentWorkbench) enables updates during the offline phase of Windows setup, speeding up an installation of Windows. Updates could also be [applied directly to a WIM](https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism-operating-system-package-servicing-command-line-options).
@@ -22,7 +22,14 @@ LatestUpdate supports PowerShell 5.0 and above and is tested on macOS, Windows 1
### PowerShell Core
-`Get-LatestUpdate`, `Get-LatestFlash`, `Get-LatestServicingStack`, `Get-LatestNetFramework` and `Save-LatestUpdate` support PowerShell Core; however, because `Import-LatestUpdate` requires the MDT Workbench, `Import-LatestUpdate` only runs under Windows PowerShell.
+Verison 3 of `LatestUpdate` has been re-written and includes full support for PowerShell Core.
+
+## Acknowledgements
+
+This module uses code and inspiration from these sources:
+
+* [Keith Garner](https://twitter.com/keithga1) - [gist](https://gist.github.com/keithga/1ad0abd1f7ba6e2f8aff63d94ab03048)
+* [Nickolaj Andersen](https://twitter.com/NickolajA) - [script](https://github.com/SCConfigMgr/ConfigMgr/blob/master/Software%20Updates/Invoke-MSLatestUpdateDownload.ps1)
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/s4g24puifpegq7kf/branch/master?svg=true&logo=PowerShell&style=flat-square
[appveyor-build]: https://ci.appveyor.com/project/aaronparker/latestupdate/
diff --git a/appveyor.yml b/appveyor.yml
index 17bf6b0..057ee9f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,5 +22,5 @@ skip_commits:
only_commits:
files:
- - LatestUpdate/*
- - tests/*
+ - LatestUpdate/**/*
+ - tests/**/*
diff --git a/tests/Main.Tests.ps1 b/tests/Main.Tests.ps1
index 4c104f3..73c80f4 100644
--- a/tests/Main.Tests.ps1
+++ b/tests/Main.Tests.ps1
@@ -1,3 +1,18 @@
+# Set variables
+If (Test-Path 'env:APPVEYOR_BUILD_FOLDER') {
+ $projectRoot = Resolve-Path -Path $env:APPVEYOR_BUILD_FOLDER
+}
+Else {
+ # Local Testing
+ $projectRoot = Resolve-Path -Path (((Get-Item (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition)).Parent).FullName)
+}
+If ($Null -eq $module ) { $module = "LatestUpdate" }
+$moduleParent = Join-Path $projectRoot $module
+$manifestPath = Join-Path $moduleParent "$module.psd1"
+$modulePath = Join-Path $moduleParent "$module.psm1"
+$modulePrivate = Join-Path $moduleParent "Private"
+$modulePublic = Join-Path $moduleParent "Public"
+Import-Module (Join-Path $projectRoot $module) -Force
Describe "General project validation" {
$scripts = Get-ChildItem (Join-Path $projectRoot $module) -Recurse -Include *.ps1, *.psm1
@@ -19,8 +34,8 @@ Describe "General project validation" {
param($file)
$analysis = Invoke-ScriptAnalyzer -Path $file.fullname -ExcludeRule @('PSAvoidGlobalVars', 'PSAvoidUsingConvertToSecureStringWithPlainText', 'PSAvoidUsingWMICmdlet') -Severity @('Warning', 'Error')
- forEach ($rule in $scriptAnalyzerRules) {
- if ($analysis.RuleName -contains $rule) {
+ ForEach ($rule in $scriptAnalyzerRules) {
+ If ($analysis.RuleName -contains $rule) {
$analysis |
Where-Object RuleName -EQ $rule -outvariable failures |
Out-Default
@@ -52,13 +67,9 @@ Describe "Function validation" {
}
# Test module and manifest
-$moduleParent = Join-Path $env:APPVEYOR_BUILD_FOLDER "LatestUpdate"
-$manifestPath = Join-Path $moduleParent "$module.psd1"
-$modulePath = Join-Path $moduleParent "$module..psm1"
-
Describe 'Module Metadata Validation' {
It 'Script fileinfo should be OK' {
- { Test-ModuleManifest $manifestPath -ErrorAction Stop } | Should Not Throw
+ { Test-ModuleManifest -Path $manifestPath -ErrorAction Stop } | Should Not Throw
}
It 'Import module should be OK' {
{ Import-Module $modulePath -Force -ErrorAction Stop } | Should Not Throw
diff --git a/tests/PrivateFunctions.Tests.ps1 b/tests/PrivateFunctions.Tests.ps1
index 271a45b..d39c28c 100644
--- a/tests/PrivateFunctions.Tests.ps1
+++ b/tests/PrivateFunctions.Tests.ps1
@@ -1,212 +1,52 @@
# Pester tests
+# Set variables
+If (Test-Path 'env:APPVEYOR_BUILD_FOLDER') {
+ $projectRoot = Resolve-Path -Path $env:APPVEYOR_BUILD_FOLDER
+}
+Else {
+ # Local Testing
+ $projectRoot = Resolve-Path -Path (((Get-Item (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition)).Parent).FullName)
+}
+If ($Null -eq $module ) { $module = "LatestUpdate" }
+$moduleParent = Join-Path $projectRoot $module
+$manifestPath = Join-Path $moduleParent "$module.psd1"
+$modulePath = Join-Path $moduleParent "$module.psm1"
+$modulePrivate = Join-Path $moduleParent "Private"
+$modulePublic = Join-Path $moduleParent "Public"
Import-Module (Join-Path $projectRoot $module) -Force
InModuleScope LatestUpdate {
- Describe 'Import-MdtModule' {
- Context "Importing the MDT PowerShell module" {
- Function Get-ValidPath {}
- Mock -CommandName Get-ValidPath -MockWith { $ProjectRoot }
- Mock Import-Module { $True }
- It "Imports the MDT PowerShell module and returns True" {
- Import-MdtModule | Should Be @($True, $True)
- }
- }
- }
-
- Describe 'New-MdtDrive' {
- $Path = "\\server\share"
- $Drive = "DS004"
- Function Get-MdtPersistentDrive {}
- Function New-PSDrive {}
- Function Add-MDTPersistentDrive {}
- Context "Creates a new MDT drive" {
- Mock -CommandName Get-MdtPersistentDrive -MockWith {
- $obj = [PSCustomObject]@{
- Name = $Drive
- Path = $Path
- Description = "MDT drive created by New-MdtDrive"
- }
- Write-Output $obj
- }
- Mock -CommandName New-PSDrive -MockWith {
- $obj = [PSCustomObject]@{
- Name = $Drive
- Provider = "MDTProvider"
- Root = $Path
- }
- Write-Output $obj
- }
- Mock -CommandName Add-MdtPersistentDrive -MockWith {
- $obj = [PSCustomObject]@{
- Name = $Drive
- Provider = "MDTProvider"
- Root = $Path
- }
- Write-Output $obj
- }
- It "Successfully creates the MDT drive" {
- New-MdtDrive -Drive $Drive -Path $Path | Should -Be $Drive
- }
- }
- }
-
- Describe 'New-MdtPackagesFolder' {
- Context "Packages folder exists" {
- Mock Test-Path { $True }
- It "Returns True if the Packages folder exists" {
- New-MdtPackagesFolder -Drive "DS001" -Path "Windows 10" | Should Be $True
- }
- }
- Context "Creates a new Packages folder" {
- Function New-Item {}
- Mock Test-Path { $False }
- Mock New-Item { $obj = [PSCustomObject]@{Name = "Windows 10"} }
- It "Successfully creates a Packages folder" {
- New-MdtPackagesFolder -Drive "DS001" -Path "Windows 10" | Should Be $True
- }
- }
- }
-
- Describe 'Get-ValidPath' {
- $RelPath = "..\LatestUpdate\"
- Context "Return valid path" {
- It "Given a relative path, it returns a fully qualified path" {
- $Path = Get-ValidPath -Path $RelPath
- $((Resolve-Path $RelPath).Path).TrimEnd("\") | Should -Be $Path
- }
- }
- Context "Fix trailing backslash" {
- It "Given a path, it returns a without a trailing backslack" {
- $Path = Get-ValidPath -Path $RelPath
- $Path.Substring($Path.Length - 1) -eq "\" | Should -Not -Be $True
- }
- }
- }
-
- Describe 'Select-LatestUpdate' {
- $Upd1 = [PSCustomObject]@{
- id = 148
- text = "KB4089848 (OS Build 16299.334)"
- level = 2
- articleId = "4089848"
- articleType = "article"
- }
- $Upd2 = [PSCustomObject]@{
- id = 144
- text = "KB4088776 (OS Build 16299.309)"
- level = 2
- articleId = "4088776"
- articleType = "article"
- }
- $KbId = @($Upd1, $Upd2)
- Context "Selects the latest update" {
- It "Given a list of updates, selects the latest one" {
- ($KbId | Select-LatestUpdate).id | Should -Be 148
- }
- }
- }
-
- Describe 'Select-UniqueUrl' {
- $Upd1 = [PSCustomObject]@{
- KB = "KB4089848"
- Note = "2018-03 Cumulative Update for Windows Server 2016 (1709) for x64-based Systems (KB4089848)"
- URL = "http://download.windowsupdate.com/d/msdownload/update/software/updt/2018/03/windows10.0-kb4089848-x64_db7c5aad31c520c6983a937c3d53170e84372b11.msu"
- }
- $Upd2 = [PSCustomObject]@{
- KB = "KB4089848"
- Note = "2018-03 Cumulative Update for Windows 10 Version 1709 for x64-based Systems (KB4089848)"
- URL = "http://download.windowsupdate.com/d/msdownload/update/software/updt/2018/03/windows10.0-kb4089848-x64_db7c5aad31c520c6983a937c3d53170e84372b11.msu"
- }
- $Updates = @($Upd1, $Upd2)
- Context "Select a single update" {
- It "Given a list of updates returns a single URL" {
- (Select-UniqueUrl -Updates $Updates).Count | Should -Be 1
- }
- }
- }
-
Describe 'Test-PSCore' {
$Version = '6.0.0'
Context "Tests whether we are running on PowerShell Core" {
It "Imports the MDT PowerShell module and returns True" {
If (($PSVersionTable.PSVersion -ge [version]::Parse($Version)) -and ($PSVersionTable.PSEdition -eq "Core")) {
- Test-PSCore | Should Be $True
+ Test-PSCore | Should -Be $True
}
}
}
Context "Tests whether we are running on Windows PowerShell" {
It "Returns False if running Windows PowerShell" {
If (($PSVersionTable.PSVersion -lt [version]::Parse($Version)) -and ($PSVersionTable.PSEdition -eq "Desktop")) {
- Test-PSCore | Should Be $False
+ Test-PSCore | Should -Be $False
}
}
}
}
+ <#
Describe 'Get-UpdateFeed' {
- [String] $StartKB = 'https://support.microsoft.com/app/content/api/content/feeds/sap/en-us/6ae59d69-36fc-8e4d-23dd-631d98bf74a9/atom'
- [String] $KB = 4483235
- $xml = Get-UpdateFeed -UpdateFeed $StartKB
+ . (Join-Path $modulePrivate "Get-ModuleResource.ps1")
+ . (Join-Path $modulePrivate "Get-UpdateFeed.ps1")
+ $Path = Join-Path $moduleParent "LatestUpdate.json"
+ Write-Host "JSON path: $Path."
+ $resourceStrings = Get-ModuleResource -Path $Path
+ $updateFeed = Get-UpdateFeed -Uri $resourceStrings.UpdateFeeds.Windows10
Context "Tests that Get-UpdateFeed returns valid XML" {
It "Returns valid XML" {
- $xml | Should -BeOfType System.Xml.XmlNode
- }
- }
- }
-
- Describe 'Get-UpdateCatalogLink' {
- $kbObj = Get-UpdateCatalogLink -KB "4483235"
- Context "Tests that Get-UpdateCatalogLink returns valid response" {
- It "Returns valid response" {
- $kbObj | Should -BeOfType Microsoft.PowerShell.Commands.WebResponseObject
- }
- }
- }
-
- Describe 'Get-KbUpdateArray' {
- $kbObj = Get-UpdateCatalogLink -KB "4483235"
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB "4483235"
- Context "Tests that Get-KbUpdateArray returns a valid array" {
- It "Returns a valid array" {
- $idTable | Should -BeOfType PSCustomObject
- }
- It "Returns an array with valid properties" {
- ForEach ($id in $idTable) {
- $id.KB.Length | Should -BeGreaterThan 0
- $id.Id.Length | Should -BeGreaterThan 0
- $id.Note.Length | Should -BeGreaterThan 0
- }
- }
- }
- }
-
- Describe 'Get-UpdateDownloadArray' {
- $kbObj = Get-UpdateCatalogLink -KB "4483235"
- $idTable = Get-KbUpdateArray -Links $kbObj.Links -KB "4483235"
- $Updates = Get-UpdateDownloadArray -IdTable $idTable
- Context "Returns a valid list of Cumulative updates" {
- It "Updates array returned should be of valid type" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
- }
- It "Updtes array returned should have a count greater than 0" {
- $Updates.Count | Should -BeGreaterThan 0
- }
- It "Returns a valid array with expected properties" {
- ForEach ($Update in $Updates) {
- $Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
- $Update.Note.Length | Should -BeGreaterThan 0
- $Update.URL.Length | Should -BeGreaterThan 0
- }
- }
- }
- }
-
- Describe 'Get-RxString' {
- Context "Returns the expected substring" {
- It "Given the string '2018-09-07T17:55:12Z', returns '2018-09-07'" {
- Get-RxString -String "2018-09-07T17:55:12Z" -RegEx "(\d{4}-\d{2}-\d{2})" | Should -Be "2018-09-07"
+ $updateFeed | Should -BeOfType System.Xml.XmlNode
}
}
}
+ #>
}
diff --git a/tests/PublicFunctions.Tests.ps1 b/tests/PublicFunctions.Tests.ps1
index 96a8671..834b492 100644
--- a/tests/PublicFunctions.Tests.ps1
+++ b/tests/PublicFunctions.Tests.ps1
@@ -1,11 +1,25 @@
# Pester tests
+# Set variables
+If (Test-Path 'env:APPVEYOR_version_FOLDER') {
+ $projectRoot = Resolve-Path -Path $env:APPVEYOR_version_FOLDER
+}
+Else {
+ # Local Testing
+ $projectRoot = Resolve-Path -Path (((Get-Item (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition)).Parent).FullName)
+}
+If ($Null -eq $module ) { $module = "LatestUpdate" }
+$moduleParent = Join-Path $projectRoot $module
+$manifestPath = Join-Path $moduleParent "$module.psd1"
+$modulePath = Join-Path $moduleParent "$module.psm1"
+$modulePrivate = Join-Path $moduleParent "Private"
+$modulePublic = Join-Path $moduleParent "Public"
Import-Module (Join-Path $projectRoot $module) -Force
-Describe 'Get-LatestUpdate' {
+Describe 'Get-LatestCumulativeUpdate' {
Context "Returns a valid list of Cumulative updates" {
- $Updates = Get-LatestUpdate
+ $Updates = Get-LatestCumulativeUpdate
It "Given no arguments, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
+ $Updates | Should -BeOfType System.Management.Automation.PSObject
}
It "Given no arguments, returns an array" {
$Updates.Count | Should -BeGreaterThan 0
@@ -13,82 +27,71 @@ Describe 'Get-LatestUpdate' {
It "Given no arguments, returns a valid array with expected properties" {
ForEach ($Update in $Updates) {
$Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
+ $Update.Architecture.Length | Should -BeGreaterThan 0
$Update.Version.Length | Should -BeGreaterThan 0
$Update.Note.Length | Should -BeGreaterThan 0
$Update.URL.Length | Should -BeGreaterThan 0
}
}
}
- Context "Windows 10: Returns expected results with -Build" {
- It "Given '18362' returns updates for build 1903" {
- $Updates = Get-LatestUpdate
+ Context "Returns expected results for each Windows 10 version" {
+ It "Given '1903' returns updates for version 1903" {
+ $Updates = Get-LatestCumulativeUpdate
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1903" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1903" | Should -Not -BeNullOrEmpty
}
}
- It "Given '17763' returns updates for build 1809" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '17763'
+ It "Given '1809' returns updates for version 1809" {
+ $Updates = Get-LatestCumulativeUpdate -Version '1809'
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1809" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1809" | Should -Not -BeNullOrEmpty
}
}
- It "Given '17134' returns updates for build 1803" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '17134'
+ It "Given '1803' returns updates for version 1803" {
+ $Updates = Get-LatestCumulativeUpdate -Version '1803'
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1803" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1803" | Should -Not -BeNullOrEmpty
}
}
- It "Given '16299' returns updates for build 1709" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '16299'
+ It "Given '1709' returns updates for version 1709" {
+ $Updates = Get-LatestCumulativeUpdate -Version '1709'
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1709" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1709" | Should -Not -BeNullOrEmpty
}
}
- It "Given '15063' returns updates for build 1703" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '15063'
+ It "Given '1703' returns updates for version 1703" {
+ $Updates = Get-LatestCumulativeUpdate -Version '1703'
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1703" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1703" | Should -Not -BeNullOrEmpty
}
}
- It "Given '14393' returns updates for build 1607" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '14393'
+ It "Given '1607' returns updates for version 1607" {
+ $Updates = Get-LatestCumulativeUpdate -Version '1607'
ForEach ($Update in $Updates) {
$Update.Note -match "Cumulative.*1607" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
$Update.Version -match "1607" | Should -Not -BeNullOrEmpty
}
}
- It "Given '10240' returns updates for build 1507" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows10 -Build '10240'
- ForEach ($Update in $Updates) {
- $Update.Note -match "Cumulative.*1507" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
- $Update.Version -match "1507" | Should -Not -BeNullOrEmpty
- }
- }
}
- Context "Returns expected results for Windows 8.1" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows8
- It "Given '-WindowsVersion Windows8' returns updates for Windows 8.1" {
- ForEach ($Update in $Updates) {
- $Update.Note -match "Security Monthly Quality Rollup*" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64" | Should -Not -BeNullOrEmpty
- $Update.Version -match "8.1|2012R2" | Should -Not -BeNullOrEmpty
- }
- }
+
+}
+
+Describe 'Get-LatestAdobeFlashUpdate' {
+ $Updates = Get-LatestAdobeFlashUpdate
+ Context "Returns a valid list of Adobe Flash Player updates" {
It "Given no arguments, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
+ $Updates | Should -BeOfType System.Management.Automation.PSObject
}
It "Given no arguments, returns an array" {
$Updates.Count | Should -BeGreaterThan 0
@@ -96,45 +99,28 @@ Describe 'Get-LatestUpdate' {
It "Given no arguments, returns a valid array with expected properties" {
ForEach ($Update in $Updates) {
$Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
+ $Update.Architecture.Length | Should -BeGreaterThan 0
$Update.Version.Length | Should -BeGreaterThan 0
$Update.Note.Length | Should -BeGreaterThan 0
$Update.URL.Length | Should -BeGreaterThan 0
}
}
}
- Context "Returns expected results for Windows 7" {
- $Updates = Get-LatestUpdate -WindowsVersion Windows7
- It "Given '-WindowsVersion Windows7' returns updates for Windows 7" {
- ForEach ($Update in $Updates) {
- $Update.Note -match "Security Monthly Quality Rollup*" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|Itanium" | Should -Not -BeNullOrEmpty
- $Update.Version -match "7|2008R2|7Embedded" | Should -Not -BeNullOrEmpty
- }
- }
- It "Given no arguments, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
- }
- It "Given no arguments, returns an array" {
- $Updates.Count | Should -BeGreaterThan 0
- }
- It "Given no arguments, returns a valid array with expected properties" {
+ Context "Returns expected results with Flash updates array" {
+ It "Given no arguments, returns updates for Adobe Flash Player" {
ForEach ($Update in $Updates) {
- $Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
- $Update.Version.Length | Should -BeGreaterThan 0
- $Update.Note.Length | Should -BeGreaterThan 0
- $Update.URL.Length | Should -BeGreaterThan 0
+ $Update.Note -match ".*Adobe Flash Player.*" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
}
}
}
}
-Describe 'Get-LatestFlash' {
- $Updates = Get-LatestFlash
- Context "Returns a valid list of Adobe Flash Player updates" {
+Describe 'Get-LatestServicingStack' {
+ $Updates = Get-LatestServicingStack
+ Context "Returns a valid list of Servicing Stack updates" {
It "Given no arguments, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
+ $Updates | Should -BeOfType System.Management.Automation.PSObject
}
It "Given no arguments, returns an array" {
$Updates.Count | Should -BeGreaterThan 0
@@ -142,87 +128,83 @@ Describe 'Get-LatestFlash' {
It "Given no arguments, returns a valid array with expected properties" {
ForEach ($Update in $Updates) {
$Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
+ $Update.Architecture.Length | Should -BeGreaterThan 0
$Update.Version.Length | Should -BeGreaterThan 0
$Update.Note.Length | Should -BeGreaterThan 0
$Update.URL.Length | Should -BeGreaterThan 0
}
}
}
- Context "Returns expected results with Flash updates array" {
- It "Given no arguments, returns updates for Adobe Flash Player" {
+ Context "Returns expected results with Servicing Stack updates array" {
+ It "Given no arguments, returns updates for Servicing Stack" {
ForEach ($Update in $Updates) {
- $Update.Note -match ".*Adobe Flash Player.*" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Note -match "Servicing stack update.*" | Should -Not -BeNullOrEmpty
+ $Update.Architecture -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
+ $Update.Version -match "1607|1703|1709|1803|1809|1903" | Should -Not -BeNullOrEmpty
}
}
}
}
-Describe 'Get-LatestServicingStack' {
- $Updates = Get-LatestServicingStack
- Context "Returns a valid list of Servicing Stack updates" {
- It "Given no arguments, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
- }
- It "Given no arguments, returns an array" {
- $Updates.Count | Should -BeGreaterThan 0
+Describe 'Get-LatestNetFrameworkUpdate' {
+ $Updates = Get-LatestNetFrameworkUpdate
+ Context "Returns a valid cumulative .NET Framework update" {
+ It "Given an OS as argument, returns an array of updates" {
+ $Updates | Should -BeOfType System.Management.Automation.PSObject
}
It "Given no arguments, returns a valid array with expected properties" {
ForEach ($Update in $Updates) {
$Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
+ $Update.Architecture.Length | Should -BeGreaterThan 0
$Update.Version.Length | Should -BeGreaterThan 0
$Update.Note.Length | Should -BeGreaterThan 0
$Update.URL.Length | Should -BeGreaterThan 0
}
}
}
- Context "Returns expected results with Servicing Stack updates array" {
- It "Given no arguments, returns updates for Servicing Stack" {
+ Context "Returns expected results with .NET Framework updates array" {
+ It "Returns a cumulative update for .NET Framework" {
ForEach ($Update in $Updates) {
- $Update.Note -match "Servicing stack update.*" | Should -Not -BeNullOrEmpty
- $Update.Arch -match "x86|x64|ARM64" | Should -Not -BeNullOrEmpty
- $Update.Version -match "1607|1703|1709|1803|1809|1903" | Should -Not -BeNullOrEmpty
+ $Update.Note -match ".*Cumulative Update for .NET Framework.*" | Should -Not -BeNullOrEmpty
}
}
}
}
-Describe 'Get-LatestNetFramework' {
- $Updates = Get-LatestNetFramework -OS "Windows Server 2019"
- Context "Returns a valid cumulative .NET Framework update" {
+Describe 'Get-LatestMonthlyRollup' {
+ $Updates = Get-LatestMonthlyRollup
+ Context "Returns a valid Windows 8.1/7 monthly rollup update" {
It "Given an OS as argument, returns an array of updates" {
- $Updates | Should -BeOfType System.Management.Automation.PSCustomObject
+ $Updates | Should -BeOfType System.Management.Automation.PSObject
}
It "Given no arguments, returns a valid array with expected properties" {
ForEach ($Update in $Updates) {
$Update.KB.Length | Should -BeGreaterThan 0
- $Update.Arch.Length | Should -BeGreaterThan 0
+ $Update.Architecture.Length | Should -BeGreaterThan 0
$Update.Version.Length | Should -BeGreaterThan 0
$Update.Note.Length | Should -BeGreaterThan 0
$Update.URL.Length | Should -BeGreaterThan 0
}
}
}
- Context "Returns expected results with Flash updates array" {
- It "Given an OS as argument, returns an cumulative update for .NET Framework" {
+ Context "Returns expected results with Monthly Rollup updates array" {
+ It "Returns a Monthly Rollup update" {
ForEach ($Update in $Updates) {
- $Update.Note -match ".*Cumulative Update for .NET Framework.*" | Should -Not -BeNullOrEmpty
+ $Update.Note -match ".*Monthly Rollup.*" | Should -Not -BeNullOrEmpty
}
}
}
}
-If (Test-Path 'env:APPVEYOR_BUILD_FOLDER') {
+If (Test-Path -Path 'env:APPVEYOR_version_FOLDER') {
# Skip download tests unless running in AppVeyor.
Describe 'Save-LatestUpdate' {
Context "Download the latest Windows 10 Cumulative updates" {
- $Updates = Get-LatestUpdate
+ $Updates = Get-LatestCumulativeUpdate
$Target = $env:TEMP
Save-LatestUpdate -Updates $Updates -Path $Target -ForceWebRequest -Verbose
- It "Given updates returned from Get-LatestUpdate, it successfully downloads the update" {
+ It "Given updates returned from Get-LatestCumulativeUpdate, it successfully downloads the update" {
ForEach ($Update in $Updates) {
$Filename = Split-Path $Update.Url -Leaf
Write-Host "Check for $(Join-Path $Target $Filename)."
@@ -231,10 +213,10 @@ If (Test-Path 'env:APPVEYOR_BUILD_FOLDER') {
}
}
Context "Download the latest Adobe Flash Player updates" {
- $Updates = Get-LatestFlash
+ $Updates = Get-LatestAdobeFlashUpdate
$Target = $env:TEMP
Save-LatestUpdate -Updates $Updates -Path $Target -ForceWebRequest -Verbose
- It "Given updates returned from Get-LatestFlash, it successfully downloads the update" {
+ It "Given updates returned from Get-LatestAdobeFlashUpdate, it successfully downloads the update" {
ForEach ($Update in $Updates) {
$Filename = Split-Path $Update.URL -Leaf
Write-Host "Check for $(Join-Path $Target $Filename)."
@@ -243,10 +225,10 @@ If (Test-Path 'env:APPVEYOR_BUILD_FOLDER') {
}
}
Context "Download the latest Servicing Stack updates" {
- $Updates = Get-LatestServicingStack
+ $Updates = Get-LatestServicingStackUpdate
$Target = $env:TEMP
Save-LatestUpdate -Updates $Updates -Path $Target -ForceWebRequest -Verbose
- It "Given updates returned from Get-LatestServicingStack, it successfully downloads the update" {
+ It "Given updates returned from Get-LatestServicingStackUpdate, it successfully downloads the update" {
ForEach ($Update in $Updates) {
$Filename = Split-Path $Update.URL -Leaf
Write-Host "Check for $(Join-Path $Target $Filename)."