Skip to content

Commit d87a64e

Browse files
authored
UpdateServicesComputerTargetGroup: New resource - managing computer target groups (#78)
1 parent 84b27b5 commit d87a64e

13 files changed

+1082
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636

3737
### Added
3838

39+
- Added UpdateServicesComputerTargetGroup Resource to manage computer target
40+
groups ([issue #44](https://github.com/dsccommunity/UpdateServicesDsc/issues/44))
3941
- Added TestKitchen files for integration tests
4042
- Added required modules, Sampler.GitHubTasks, powershell-yaml
4143
- Added wildcard support in Products parameter of UpdatesServicesServer resource.
42-
(issue #13)
44+
([issue #13](https://github.com/dsccommunity/UpdateServicesDsc/issues/13))
4345

4446
### Fixed
4547

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
# DSC resource to manage WSUS Computer Target Groups.
2+
3+
# Load Common Module
4+
$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common'
5+
Import-Module -Name $script:resourceHelperModulePath
6+
$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'
7+
8+
9+
<#
10+
.SYNOPSIS
11+
Retrieves the current state of the WSUS Computer Target Group.
12+
13+
.DESCRIPTION
14+
This function retrieves the current state of a WSUS Computer Target Group
15+
by querying the WSUS server and validating the group's path.
16+
17+
.PARAMETER Name
18+
The Name of the WSUS Computer Target Group.
19+
20+
.PARAMETER Path
21+
The Path to the WSUS Computer Target Group in the format 'Parent/Child'.
22+
#>
23+
function Get-TargetResource
24+
{
25+
[CmdletBinding()]
26+
[OutputType([System.Collections.Hashtable])]
27+
param
28+
(
29+
[Parameter(Mandatory = $true)]
30+
[System.String]
31+
$Name,
32+
33+
[Parameter(Mandatory = $true)]
34+
[System.String]
35+
$Path
36+
)
37+
38+
try
39+
{
40+
$WsusServer = Get-WsusServer
41+
}
42+
catch
43+
{
44+
New-InvalidOperationException -Message $script:localizedData.WSUSConfigurationFailed -ErrorRecord $_
45+
}
46+
47+
$Ensure = 'Absent'
48+
$Id = $null
49+
50+
if ($null -ne $WsusServer)
51+
{
52+
Write-Verbose -Message ($script:localizedData.GetWsusServerSucceeded -f $WsusServer.Name)
53+
$ComputerTargetGroup = $WsusServer.GetComputerTargetGroups().Where({ $_.Name -eq $Name }) | Select-Object -First 1
54+
55+
if ($null -ne $ComputerTargetGroup)
56+
{
57+
$ComputerTargetGroupPath = Get-ComputerTargetGroupPath -ComputerTargetGroup $ComputerTargetGroup
58+
if ($Path -eq $ComputerTargetGroupPath)
59+
{
60+
$Ensure = 'Present'
61+
$Id = $ComputerTargetGroup.Id.Guid
62+
Write-Verbose -Message ($script:localizedData.FoundComputerTargetGroup -f $Name, $Path, $Id)
63+
}
64+
else
65+
{
66+
# ComputerTargetGroup Names must be unique within the overall hierarchy
67+
New-InvalidOperationException -Message ($script:localizedData.DuplicateComputerTargetGroup -f $ComputerTargetGroup.Name, $ComputerTargetGroupPath)
68+
}
69+
}
70+
}
71+
else
72+
{
73+
Write-Verbose -Message $script:localizedData.GetWsusServerFailed
74+
}
75+
76+
if ($null -eq $Id)
77+
{
78+
Write-Verbose -Message ($script:localizedData.NotFoundComputerTargetGroup -f $Name, $Path)
79+
}
80+
81+
$returnValue = @{
82+
Ensure = $Ensure
83+
Name = $Name
84+
Path = $Path
85+
Id = $Id
86+
}
87+
88+
return $returnValue
89+
}
90+
91+
<#
92+
.SYNOPSIS
93+
Sets the state of the WSUS Computer Target Group.
94+
95+
.DESCRIPTION
96+
This function creates or removes a WSUS Computer Target Group based on
97+
the Ensure parameter. It validates the parent path and performs the
98+
appropriate action on the WSUS server.
99+
100+
.PARAMETER Ensure
101+
Determines if the Computer Target Group should be created or removed.
102+
Accepts 'Present' (default) or 'Absent'.
103+
104+
.PARAMETER Name
105+
Name of the Computer Target Group.
106+
107+
.PARAMETER Path
108+
The Path to the Computer Target Group in the format 'Parent/Child'.
109+
#>
110+
function Set-TargetResource
111+
{
112+
[CmdletBinding()]
113+
param
114+
(
115+
[Parameter()]
116+
[ValidateSet('Present', 'Absent')]
117+
[System.String]
118+
$Ensure = 'Present',
119+
120+
[Parameter(Mandatory = $true)]
121+
[System.String]
122+
$Name,
123+
124+
[Parameter(Mandatory = $true)]
125+
[System.String]
126+
$Path
127+
)
128+
129+
try
130+
{
131+
$WsusServer = Get-WsusServer
132+
}
133+
catch
134+
{
135+
New-InvalidOperationException -Message $script:localizedData.WSUSConfigurationFailed -ErrorRecord $_
136+
}
137+
138+
# break down path to identify the parent computer target group based on name and its own unique path
139+
$ParentComputerTargetGroupName = (($Path -split '/')[-1])
140+
$ParentComputerTargetGroupPath = ($Path -replace "[/]$ParentComputerTargetGroupName", '')
141+
142+
if ($null -ne $WsusServer)
143+
{
144+
$ParentComputerTargetGroups = $WsusServer.GetComputerTargetGroups().Where({
145+
$_.Name -eq $ParentComputerTargetGroupName
146+
}) | Select-Object -First 1
147+
148+
if ($null -ne $ParentComputerTargetGroups)
149+
{
150+
foreach ($ParentComputerTargetGroup in $ParentComputerTargetGroups)
151+
{
152+
$ComputerTargetGroupPath = Get-ComputerTargetGroupPath -ComputerTargetGroup $ParentComputerTargetGroup
153+
if ($ParentComputerTargetGroupPath -eq $ComputerTargetGroupPath)
154+
{
155+
# parent Computer Target Group Exists
156+
Write-Verbose -Message ($script:localizedData.FoundParentComputerTargetGroup -f $ParentComputerTargetGroupName, `
157+
$ParentComputerTargetGroupPath, $ParentComputerTargetGroup.Id.Guid)
158+
159+
# create the new Computer Target Group if Ensure -eq 'Present'
160+
if ($Ensure -eq 'Present')
161+
{
162+
try
163+
{
164+
$null = $WsusServer.CreateComputerTargetGroup($Name, $ParentComputerTargetGroup)
165+
Write-Verbose -Message ($script:localizedData.CreateComputerTargetGroupSuccess -f $Name, $Path)
166+
return
167+
}
168+
catch
169+
{
170+
New-InvalidOperationException -Message (
171+
$script:localizedData.CreateComputerTargetGroupFailed -f $Name, $Path
172+
) -ErrorRecord $_
173+
}
174+
}
175+
else
176+
{
177+
# $Ensure -eq 'Absent' - must call the Delete() method on the group itself for removal
178+
$ChildComputerTargetGroup = $ParentComputerTargetGroup.GetChildTargetGroups().Where({
179+
$_.Name -eq $Name
180+
}) | Select-Object -First 1
181+
182+
if ($null -eq $ChildComputerTargetGroup)
183+
{
184+
# Already absent
185+
Write-Verbose -Message ($script:localizedData.NotFoundComputerTargetGroup -f $Name, $Path)
186+
return
187+
}
188+
189+
try
190+
{
191+
$childId = $ChildComputerTargetGroup.Id.Guid
192+
$null = $ChildComputerTargetGroup.Delete()
193+
Write-Verbose -Message ($script:localizedData.DeleteComputerTargetGroupSuccess -f $Name, $childId, $Path)
194+
return
195+
}
196+
catch
197+
{
198+
$childId = if ($ChildComputerTargetGroup)
199+
{
200+
$ChildComputerTargetGroup.Id.Guid
201+
}
202+
else
203+
{
204+
'N/A'
205+
}
206+
New-InvalidOperationException -Message (
207+
$script:localizedData.DeleteComputerTargetGroupFailed -f $Name, $childId, $Path
208+
) -ErrorRecord $_
209+
}
210+
}
211+
}
212+
}
213+
}
214+
215+
New-InvalidOperationException -Message ($script:localizedData.NotFoundParentComputerTargetGroup -f $ParentComputerTargetGroupName, `
216+
$ParentComputerTargetGroupPath, $Name)
217+
}
218+
else
219+
{
220+
Write-Verbose -Message $script:localizedData.GetWsusServerFailed
221+
}
222+
}
223+
224+
<#
225+
.SYNOPSIS
226+
Tests the current state of the WSUS Computer Target Group.
227+
228+
.DESCRIPTION
229+
This function determines whether the WSUS Computer Target Group is in
230+
the desired state by comparing the current state to the requested Ensure value.
231+
232+
.PARAMETER Ensure
233+
Determines if the Computer Target Group should be created or removed.
234+
Accepts 'Present' (default) or 'Absent'.
235+
236+
.PARAMETER Name
237+
Name of the Computer Target Group
238+
239+
.PARAMETER Path
240+
The Path to the Computer Target Group in the format 'Parent/Child'.
241+
#>
242+
function Test-TargetResource
243+
{
244+
[CmdletBinding()]
245+
[OutputType([System.Boolean])]
246+
param
247+
(
248+
[Parameter()]
249+
[ValidateSet('Present', 'Absent')]
250+
[System.String]
251+
$Ensure = 'Present',
252+
253+
[Parameter(Mandatory = $true)]
254+
[System.String]
255+
$Name,
256+
257+
[Parameter(Mandatory = $true)]
258+
[System.String]
259+
$Path
260+
)
261+
262+
$result = Get-TargetResource -Name $Name -Path $Path
263+
264+
if ($Ensure -eq $result.Ensure)
265+
{
266+
Write-Verbose -Message ($script:localizedData.ResourceInDesiredState -f $Name, $Path, $result.Ensure)
267+
return $true
268+
}
269+
else
270+
{
271+
Write-Verbose -Message ($script:localizedData.ResourceNotInDesiredState -f $Name, $Path, $result.Ensure)
272+
return $false
273+
}
274+
}
275+
276+
277+
<#
278+
.SYNOPSIS
279+
Gets the Computer Target Group Path within WSUS by recursing up through each Parent Computer Target Group
280+
281+
.DESCRIPTION
282+
This function recursively traverses the parent hierarchy of a WSUS Computer
283+
Target Group to construct its full path in the format 'Parent/Child/GrandChild'.
284+
285+
.PARAMETER ComputerTargetGroup
286+
The Computer Target Group object for which to retrieve the path.
287+
288+
.OUTPUTS
289+
System.String
290+
291+
Returns the full hierarchical path of the Computer Target Group.
292+
#>
293+
function Get-ComputerTargetGroupPath
294+
{
295+
[CmdletBinding()]
296+
[OutputType([System.String])]
297+
param
298+
(
299+
[Parameter(Mandatory = $true)]
300+
[System.Object]
301+
$ComputerTargetGroup
302+
)
303+
304+
if ($ComputerTargetGroup.Name -eq 'All Computers')
305+
{
306+
return 'All Computers'
307+
}
308+
309+
$computerTargetGroupPath = ''
310+
$computerTargetGroupParents = @()
311+
$moreParentContainers = $true
312+
$x = 0
313+
314+
do
315+
{
316+
try
317+
{
318+
$ComputerTargetGroup = $ComputerTargetGroup.GetParentTargetGroup()
319+
$computerTargetGroupParents += $ComputerTargetGroup.Name
320+
}
321+
catch
322+
{
323+
# 'All Computers' container throws an exception when GetParentTargetGroup() method called
324+
$moreParentContainers = $false
325+
}
326+
327+
$x++
328+
} while ($moreParentContainers -and ($x -lt 20))
329+
330+
for ($i = ($computerTargetGroupParents.Count - 1); $i -ge 0; $i--)
331+
{
332+
if (-not [string]::IsNullOrEmpty($computerTargetGroupPath))
333+
{
334+
$computerTargetGroupPath += ('/' + $computerTargetGroupParents[$i])
335+
}
336+
else
337+
{
338+
$computerTargetGroupPath += $computerTargetGroupParents[$i]
339+
}
340+
}
341+
342+
return $computerTargetGroupPath
343+
}
344+
345+
Export-ModuleMember -Function *-TargetResource
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[ClassVersion("1.0.0.0"), FriendlyName("UpdateServicesComputerTargetGroup")]
2+
class DSC_UpdateServicesComputerTargetGroup : OMI_BaseResource
3+
{
4+
[Write, Description("An enumerated value that describes if the WSUS Computer Target Group is configured. Default value is 'Present'."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
5+
[Key, Description("Name of the Computer Target Group.")] String Name;
6+
[Key, Description("Path to the Computer Target Group.")] String Path;
7+
[Read, Description("ID / GUID of the Computer Target Group.")] String Id;
8+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Localized Strings for UpdateServicesComputerTargetGroup resource
2+
ConvertFrom-StringData @'
3+
GetWsusServerFailed = Get-WsusServer failed to return a WSUS Server. The server may not yet have been configured. (USCTG0001)
4+
WSUSConfigurationFailed = WSUS Computer Target Group configuration failed. (USCTG0002)
5+
GetWsusServerSucceeded = WSUS Server information has been successfully retrieved from server '{0}'. (USCTG0003)
6+
NotFoundComputerTargetGroup = A Computer Target Group with Name '{0}' was not found at Path '{1}'. (USCTG0004)
7+
DuplicateComputerTargetGroup = A Computer Target Group with Name '{0}' already exists at Path '{1}'. (USCTG0005)
8+
FoundComputerTargetGroup = Successfully located Computer Target Group with Name '{0}' at Path '{1}' with ID '{2}'. (USCTG0006)
9+
ResourceInDesiredState = The Computer Target Group '{0}' at Path '{1}' is '{2}' which is the desired state. (USCTG0007)
10+
ResourceNotInDesiredState = The Computer Target Group '{0}' at Path '{1}' is '{2}' which is NOT the desired state. (USCTG0008)
11+
FoundParentComputerTargetGroup = Successfully located Parent Computer Target Group with Name '{0}' at Path '{1}' with ID '{2}'. (USCTG0009)
12+
NotFoundParentComputerTargetGroup = The Parent Computer Target Group with Name '{0}' was not found at Path '{1}'. The new Computer Target Group '{2}' cannot be created. (USCTG0010)
13+
CreateComputerTargetGroupFailed = An error occurred creating the Computer Target Group '{0}' at Path '{1}'. (USCTG0011)
14+
CreateComputerTargetGroupSuccess = The Computer Target Group '{0}' was successfully created at Path '{1}'. (USCTG0012)
15+
DeleteComputerTargetGroupFailed = An error occurred deleting the Computer Target Group '{0}' with ID '{1}' from Path '{2}'. (USCTG0013)
16+
DeleteComputerTargetGroupSuccess = The Computer Target Group '{0}' with ID '{1}' was successfully deleted from Path '{2}'. (USCTG0014)
17+
'@

0 commit comments

Comments
 (0)