Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- UpdateServicesServer
- BREAKING CHANGE: All parameters will now only be set when specifically applied
rather than defaulting to hardcoded values if left undefined.
In particular set ContentDir, Languages, Products, Classifications as needed.
Fixes [issue #55](https://github.com/dsccommunity/UpdateServicesDsc/issues/55)
- Updated initial offline package sync WSUS.cab.
- Changed azure pipeline to use latest version of ubuntu and change the management
of pipeline artifact
Expand Down Expand Up @@ -36,6 +41,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- UpdateServicesServer
- Added support for the following settings:
- ContentDir can be set to empty string for clients to download from Microsoft Update.
- Updates are downloaded only when they are approved.
- Express installation packages should be downloaded.
- Update binaries are downloaded from Microsoft Update instead of from the
upstream server.
Fixes [issue #39](https://github.com/dsccommunity/UpdateServicesDsc/issues/39)
- WSUS infrastructure updates are approved automatically.
- The latest revision of an update should be approved automatically.
- An update should be automatically declined when it is revised to be expired
and AutoRefreshUpdateApprovals is enabled.
- The downstream server should roll up detailed computer and update status information.
- Email status notifications and SMTP settings, including status notifications DST fix.
Fixes [issue #15](https://github.com/dsccommunity/UpdateServicesDsc/issues/15)
- Use Xpress Encoding to compress update metadata.
- Use foreground priority for BITS downloads
- The maximum .cab file size (in megabytes) that Local Publishing will create.
- The maximum number of concurrent update downloads.
- Added UpdateServicesComputerTargetGroup Resource to manage computer target
groups ([issue #44](https://github.com/dsccommunity/UpdateServicesDsc/issues/44))
- Added TestKitchen files for integration tests
Expand All @@ -45,6 +69,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- UpdateServicesApprovalRule
- Before running, ensure that UpdateServices PowerShell module is installed.
- Updated error handling to specifically catch errors if WSUS Server is unavailable.
- Added check to make sure Post Install was successful before trying to get resource.
- Fix issue [#63](https://github.com/dsccommunity/UpdateServicesDsc/issues/63)
Broken verbose output for WSUS server name.
- Fix issue [#61](https://github.com/dsccommunity/UpdateServicesDsc/issues/61)
Allow multiple product categories with same name (e.g. "Windows Admin Center")
- Removed ErrorRecord from New-InvalidOperationException outside of try / catch.
- UpdateServicesCleanup
- Fix issue [#93](https://github.com/dsccommunity/UpdateServicesDsc/issues/93)
Allow UpdateServicesCleanup resource to test and update TimeOfDay as needed.
- UpdateServicesComputerTargetGroup
- Before running, ensure that UpdateServices PowerShell module is installed.
- Updated error handling to specifically catch errors if WSUS Server is unavailable.
- Added check to make sure Post Install was successful before trying to get resource.
- UpdateServicesServer
- Before running, ensure that UpdateServices PowerShell module is installed.
- Updated error handling to specifically catch errors if WSUS Server is unavailable.
- Added check to make sure Post Install was successful before trying to get resource.
- Update setting dependency logic to stop incompatible settings being set / returned.
- Get Languages as a string array instead of comma separated values.
Fix issue [#76](https://github.com/dsccommunity/UpdateServicesDsc/issues/76)
- Stopped PDT.psm1 returning boolean 'true' alongside normal output as creating process.
- Fix deploy job in AzurePipeline, Added Sampler.GithubTasks in build.yaml
- Fix issue #61 and #67, with add a foreach loop when `Set-TargetResource` found
multiple products for the same `Title`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,27 @@ function Get-TargetResource
$Name
)

Assert-Module -ModuleName UpdateServices

try
{
$WsusServer = Get-WsusServer
$Ensure = 'Absent'
$Classifications = $null
$Products = $null
$ComputerGroups = $null
$Enabled = $null
}
catch
{
Write-Verbose -Message $script:localizedData.GetWsusServerFailed
}

$Ensure = 'Absent'
$Classifications = $null
$Products = $null
$ComputerGroups = $null
$Enabled = $null

if ($null -ne $WsusServer)
try {
if (($null -ne $WsusServer) -and `
(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup\Installed Role Services" `
-Name 'UpdateServices-Services' -ErrorAction Stop).'UpdateServices-Services' -eq '2')
{
Write-Verbose -Message ('Identified WSUS server information: {0}' -f $WsusServer.Name)

Expand Down Expand Up @@ -177,6 +188,8 @@ function Set-TargetResource
$RunRuleNow
)

Assert-Module -ModuleName UpdateServices

try
{
if ($WsusServer = Get-WsusServer)
Expand Down Expand Up @@ -220,11 +233,14 @@ function Set-TargetResource
$ApprovalRule.Save()

$ProductCollection = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateCategoryCollection
$AllWsusProducts = $WsusServer.GetUpdateCategories()
foreach ($Product in $Products)
{
if ($WsusProduct = Get-WsusProduct | Where-Object -FilterScript { $_.Product.Title -eq $Product })
if ($WsusProduct = $AllWsusProducts | Where-Object -FilterScript { $_.Title -eq $Product })
{
$ProductCollection.Add($WsusServer.GetUpdateCategory($WsusProduct.Product.Id))
$WsusProduct | Foreach-Object {
$ProductCollection.Add($_)
}
}
}

Expand Down Expand Up @@ -262,7 +278,7 @@ function Set-TargetResource
{
New-InvalidOperationException -Message (
$script:localizedData.RuleFailedToCreate -f $Name
) -ErrorRecord $_
)
}
}
'Absent'
Expand Down Expand Up @@ -386,6 +402,8 @@ function Test-TargetResource
$RunRuleNow
)

Assert-Module -ModuleName UpdateServices

$result = $true

$ApprovalRule = Get-TargetResource -Name $Name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function Get-TargetResource
}
}
}
$TimeOfDay = $Task.Triggers.StartBoundary.Split('T')[1]
$TimeOfDay = ([datetimeoffset]$Task.Triggers[0].StartBoundary).TimeOfDay.ToString('c')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for potential null reference and parsing errors.

The code accesses $Task.Triggers[0] without verifying the array has elements and casts StartBoundary to [datetimeoffset] without error handling. Either condition could cause runtime errors.

Consider wrapping this in a try-catch block or adding validation:

-            $TimeOfDay = ([datetimeoffset]$Task.Triggers[0].StartBoundary).TimeOfDay.ToString('c')
+            if ($Task.Triggers -and $Task.Triggers.Count -gt 0)
+            {
+                try
+                {
+                    $TimeOfDay = ([datetimeoffset]$Task.Triggers[0].StartBoundary).TimeOfDay.ToString('c')
+                }
+                catch
+                {
+                    Write-Verbose -Message "Failed to parse TimeOfDay from scheduled task trigger."
+                }
+            }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In source/DSCResources/DSC_UpdateServicesCleanup/DSC_UpdateServicesCleanup.psm1
around line 69, the code directly indexes $Task.Triggers[0] and casts
StartBoundary to [datetimeoffset] which can throw null-reference or parse
exceptions; add validation to ensure $Task.Triggers is not null/empty and that
$Task.Triggers[0].StartBoundary is populated, and wrap the cast/parsing in a
try/catch (or use [datetimeoffset]::TryParse) to safely obtain TimeOfDay,
logging or defaulting when validation/parse fails so the function won’t error at
runtime.

}
else
{
Expand Down Expand Up @@ -363,6 +363,12 @@ function Test-TargetResource
Write-Verbose -Message $script:localizedData.CleanupPublishedTestFailed
$result = $false
}

if ($CleanupTask.TimeOfDay -ne $TimeOfDay)
{
Write-Verbose -Message $script:localizedData.TimeOfDayTestFailed
$result = $false
}
}

$result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ CompressTestFailed = Compress Updates test failed.
CleanupObsoleteCptTestFailed= Cleanup Obsolete Computers test failed.
CleanupContentTestFailed = Cleanup Unneeded Content Files test failed.
CleanupPublishedTestFailed = Cleanup Local Published Content Files test failed.
TimeOfDayTestFailed = Time of Day test failed.
TestFailedAfterSet = Test-TargetResource returned false after calling set.
'@
Loading
Loading