@@ -43,74 +43,108 @@ function GetValidCimProperties {
43
43
[Parameter (Mandatory = $true , ValueFromPipeline = $true )]
44
44
[Microsoft.Management.Infrastructure.CimClass ]$CimClass ,
45
45
46
+ [Parameter (Mandatory = $true )]
47
+ $ClassName ,
48
+
46
49
[Parameter ()]
47
50
[object ]$Properties ,
48
51
49
52
[Parameter ()]
50
- [ValidateSet (' Get' , ' Set' , ' Test' )]
51
- [string ]$Operation
53
+ [switch ] $SkipReadOnly ,
54
+
55
+ [Parameter ()]
56
+ [switch ] $ValidateKeyProperty
52
57
)
53
58
54
- $validatedProperties = [System.Collections.Generic.List [Object ]]::new()
55
-
56
- switch ($Operation ) {
57
- ' Get' {
58
- # For 'Get', we don't need to validate properties, just return all properties
59
- $cimClass.CimClassProperties | ForEach-Object {
60
- $validatedProperties.Add ([PSCustomObject ]@ {
61
- Name = $_.Name
62
- Type = $_.CimType.ToString ()
63
- IsKey = $_.Flags -contains ' Key'
64
- IsReadOnly = $_.Flags -contains ' ReadOnly'
65
- })
66
- }
59
+ $availableProperties = $CimClass.CimClassProperties | Where-Object - Property Name -in $Properties.psobject.Properties.name
60
+ $validatedProperties = [System.Collections.Generic.List [Array ]]::new()
61
+
62
+ $keyProperty = ($availableProperties.flags -like " Property, Key*" )
63
+
64
+ if ($null -eq $availableProperties ) {
65
+ " No valid properties found in the CIM class '$ClassName ' for the provided properties." | Write-DscTrace - Operation Error
66
+ exit 1
67
+ }
68
+
69
+ if ($null -eq $keyProperty ) {
70
+ " Key property not provided for CIM class '$ClassName '." | Write-DscTrace - Operation Error
71
+ exit 1
72
+ }
73
+
74
+ if ($ValidateKeyProperty.IsPresent ) {
75
+ # Check if any key property is also read-only
76
+ $keyProps = $availableProperties | Where-Object { $_.Flags.ToString () -like " *Key*" }
77
+ $readOnlyKeyProps = $keyProps | Where-Object { $_.Flags.ToString () -like " *ReadOnly*" }
78
+
79
+ if ($readOnlyKeyProps.Count -eq $keyProps.Count ) {
80
+ " All key properties in the CIM class '$ClassName ' are read-only, which is not supported." | Write-DscTrace - Operation Error
81
+ exit 1
67
82
}
68
- ' Set' {
69
- # For 'Set', we need to validate that the provided properties match the CIM class
70
- $availableProperties = $cimClass.CimClassProperties | ForEach-Object {
71
- [string []]$flags = $_.Flags.ToString ().Split(" ," ).Trim()
72
- if ($flags -notcontains ' ReadOnly' -or $flags -contains ' Key' ) {
73
- @ {
74
- Name = $_.Name
75
- Type = $_.CimType
76
- Flags = $flags
77
- IsKey = $flags -contains ' Key'
78
- IsReadOnly = $flags -contains ' ReadOnly' # This is to ensure we identify key read-only properties
79
- }
80
- }
83
+ }
84
+
85
+ # Check if the provided properties match the available properties in the CIM class
86
+ # If the count of provided properties does not match the available properties, we log a warning but continue
87
+ if ($properties.psobject.Properties.name.count -ne $availableProperties.Count ) {
88
+ $inputPropertyNames = $properties.psobject.Properties.Name
89
+ $availablePropertyNames = $availableProperties.Name
90
+
91
+ $missingProperties = $inputPropertyNames | Where-Object { $_ -notin $availablePropertyNames }
92
+ if ($missingProperties ) {
93
+ foreach ($missing in $missingProperties ) {
94
+ " Property '$missing ' was provided but not found in the CIM class '$ ( $CimClass.ClassName ) '." | Write-DscTrace - Operation Warn
81
95
}
96
+ }
97
+ }
82
98
83
- $validatedProperties = [System.Collections.Generic.List [Object ]]::new()
84
- foreach ($property in $availableProperties ) {
85
- $propName = $property.Name
86
- $isKey = $property.IsKey
87
- $isReadOnly = $property.IsReadOnly
88
-
89
- if ($isKey ) {
90
- if ($Properties.psobject.properties.name -notcontains $propName -or $null -eq $properties .$propName -or $Properties .$propName -eq ' ' ) {
91
- " Key property '$propName ' is required but not provided or is empty." | Write-DscTrace - Operation Error
92
- exit 1
93
- } else {
94
- $validatedProperties.Add ([PSCustomObject ]@ {
95
- Name = $propName
96
- Value = $Properties .$propName
97
- Type = $property.Type
98
- IsReadOnly = $isReadOnly
99
- })
100
- }
101
- } elseif ($Properties.psobject.Properties.name -contains $propName ) {
102
- $validatedProperties.Add ([PSCustomObject ]@ {
103
- Name = $propName
104
- Value = $Properties .$propName
105
- Type = $property.Type
106
- IsReadOnly = $isReadOnly
107
- })
108
- } else {
109
- " Property '$propName ' is not provided in the resource object." | Write-DscTrace - Operation Trace
99
+ $validatedProperties.Add ($availableProperties )
100
+
101
+ if ($SkipReadOnly.IsPresent ) {
102
+ $availableProperties = foreach ($prop in $availableProperties ) {
103
+ [string []]$flags = $prop.Flags.ToString ().Split(" ," ).Trim()
104
+ if ($null -ne $properties .$ ($prop.Name )) {
105
+ # Filter out read-only properties if SkipReadOnly is specified
106
+ if ($flags -notcontains ' ReadOnly' ) {
107
+ $prop
110
108
}
109
+ } else {
110
+ # Return $prop as if there is an empty value provided as property, we are not going to a WHERE clause
111
+ $prop
111
112
}
112
113
}
113
- }
114
+
115
+ return $availableProperties
116
+ }
117
+
118
+ # if ($SkipReadOnly.IsPresent) {
119
+ # # For 'Set', we need to validate that the provided properties match the CIM class
120
+ # $availableProperties = $cimClass.CimClassProperties | ForEach-Object {
121
+ # [string[]]$flags = $_.Flags.ToString().Split(",").Trim()
122
+ # if ($flags -notcontains 'ReadOnly' -or $flags -contains 'Key') {
123
+ # $_
124
+ # }
125
+ # }
126
+
127
+ # # Reset the validated properties list as we only want to capture non-readonly properties for 'Set'
128
+ # $validatedProperties = [System.Collections.Generic.List[Array]]::new()
129
+ # foreach ($property in $availableProperties) {
130
+ # $propName = $property.Name
131
+ # $isKey = $property.IsKey
132
+
133
+ # if ($isKey) {
134
+ # # Still check here if the key property is passed as we continue
135
+ # if ($Properties.psobject.properties.name -notcontains $propName -or $null -eq $properties.$propName -or $Properties.$propName -eq '') {
136
+ # "Key property '$propName' is required but not provided or is empty." | Write-DscTrace -Operation Error
137
+ # exit 1
138
+ # } else {
139
+ # $validatedProperties.Add($property)
140
+ # }
141
+ # } elseif ($Properties.psobject.Properties.name -contains $propName) {
142
+ # $validatedProperties.Add($property)
143
+ # } else {
144
+ # "Property '$propName' is not provided in the resource object." | Write-DscTrace -Operation Trace
145
+ # }
146
+ # }
147
+ # }
114
148
115
149
return $validatedProperties
116
150
}
@@ -120,11 +154,7 @@ function GetWmiInstance {
120
154
param
121
155
(
122
156
[Parameter (Mandatory = $true , ValueFromPipeline = $true )]
123
- [psobject ]$DesiredState ,
124
-
125
- [Parameter ()]
126
- [ValidateSet (' Get' , ' Set' , ' Test' )]
127
- [string ]$Operation = ' Get'
157
+ [psobject ]$DesiredState
128
158
)
129
159
130
160
$type_fields = $DesiredState.type -split " /"
@@ -134,36 +164,41 @@ function GetWmiInstance {
134
164
$class = Get-CimClass - Namespace $wmi_namespace - ClassName $wmi_classname - ErrorAction Stop
135
165
136
166
if ($DesiredState.properties ) {
137
- $properties = GetValidCimProperties - CimClass $class - Properties $DesiredState.properties - Operation $Operation
167
+ $properties = GetValidCimProperties - CimClass $class - ClassName $wmi_classname - Properties $DesiredState.properties - SkipReadOnly
138
168
139
169
$query = " SELECT $ ( $properties.Name -join ' ,' ) FROM $wmi_classname "
140
170
$where = " WHERE "
141
171
$useWhere = $false
142
172
$first = $true
143
173
foreach ($property in $properties ) {
144
174
# TODO: validate property against the CIM class to give better error message
145
- if ($null -ne $property.value ) {
175
+ if ($null -ne $DesiredState .properties . $ ( $ property.Name ) ) {
146
176
$useWhere = $true
147
177
if ($first ) {
148
178
$first = $false
149
179
} else {
150
180
$where += " AND "
151
181
}
152
182
153
- if ($property.Type -eq " String" ) {
154
- $where += " $ ( $property.Name ) = '$ ( $property.Value ) '"
183
+ if ($property.CimType -eq " String" ) {
184
+ $where += " $ ( $property.Name ) = '$ ( $DesiredState .properties . $ ( $ property.Name ) ) '"
155
185
} else {
156
- $where += " $ ( $property.Name ) = $ ( $property.Value ) "
186
+ $where += " $ ( $property.Name ) = $ ( $DesiredState .properties . $ ( $ property.Name ) ) "
157
187
}
158
188
}
159
189
}
160
190
if ($useWhere ) {
161
191
$query += $where
162
192
}
163
193
" Query: $query " | Write-DscTrace - Operation Debug
164
- $wmi_instances = Get-CimInstance - Namespace $wmi_namespace - Query $query - ErrorAction Stop
194
+ $wmi_instances = Get-CimInstance - Namespace $wmi_namespace - Query $query - ErrorAction Ignore - ErrorVariable err
165
195
} else {
166
- $wmi_instances = Get-CimInstance - Namespace $wmi_namespace - ClassName $wmi_classname - ErrorAction Stop
196
+ $wmi_instances = Get-CimInstance - Namespace $wmi_namespace - ClassName $wmi_classname - ErrorAction Ignore - ErrorVariable Err
197
+ }
198
+
199
+ if ($err ) {
200
+ " Error retrieving WMI instances: $ ( $err.Exception.Message ) " | Write-DscTrace - Operation Error
201
+ exit 1
167
202
}
168
203
169
204
return $wmi_instances
@@ -194,12 +229,10 @@ function GetCimSpace {
194
229
195
230
switch ($Operation ) {
196
231
' Get' {
197
- # TODO: identify key properties and add WHERE clause to the query
198
232
$wmi_instances = GetWmiInstance - DesiredState $DesiredState
199
233
200
234
if ($wmi_instances ) {
201
235
$instance_result = [ordered ]@ {}
202
- # TODO: for a `Get`, they key property must be provided so a specific instance is returned rather than just the first
203
236
$wmi_instance = $wmi_instances [0 ] # for 'Get' we return just first matching instance; for 'export' we return all instances
204
237
$wmi_instance.psobject.properties | ForEach-Object {
205
238
if (($_.Name -ne " type" ) -and (-not $_.Name.StartsWith (" Cim" ))) {
@@ -225,14 +258,14 @@ function GetCimSpace {
225
258
226
259
$wmi_instance.Properties | ForEach-Object {
227
260
if ($r.properties.psobject.properties.name -contains $_.Name ) {
228
- $properties [$_.Name ] = $_ .Value
261
+ $properties [$_.Name ] = $r .properties . $ ( $_ .Name )
229
262
}
230
263
}
231
264
232
- $readOnlyProperties = $wmi_instance.Properties | Where-Object - Property IsReadOnly -eq $true
265
+ $readOnlyProperties = $wmi_instance.Properties | Where-Object - Property Flags -like " *ReadOnly* "
233
266
234
267
if ($null -eq $wmi_instance.CimInstance ) {
235
- New-CimInstance - Namespace $wmi_instance.Namespace - ClassName $wmi_instance.ClassName - Property $properties - ErrorAction Stop
268
+ $instance = New-CimInstance - Namespace $wmi_instance.Namespace - ClassName $wmi_instance.ClassName - Property $properties - ErrorAction Ignore - ErrorVariable err
236
269
} else {
237
270
# When calling Set-CimInstance, the read-only properties needs to be filtered out
238
271
if ($readOnlyProperties ) {
@@ -242,7 +275,7 @@ function GetCimSpace {
242
275
}
243
276
}
244
277
}
245
- $wmi_instance.CimInstance | Set-CimInstance - Property $properties - ErrorAction Stop
278
+ $wmi_instance.CimInstance | Set-CimInstance - Property $properties - ErrorAction Ignore - ErrorVariable err | Out-Null
246
279
}
247
280
248
281
$addToActualState = [dscResourceObject ]@ {
@@ -281,9 +314,9 @@ function ValidateCimMethodAndArguments {
281
314
exit 1
282
315
}
283
316
284
- $validatedProperties = GetValidCimProperties - CimClass $cimClass - Properties $DesiredState.properties - Operation Set
317
+ $validatedProperties = GetValidCimProperties - CimClass $cimClass - ClassName $className - Properties $DesiredState.properties - ValidateKeyProperty
285
318
286
- $cimInstance = GetWmiInstance - DesiredState $DesiredState - Operation Set
319
+ $cimInstance = GetWmiInstance - DesiredState $DesiredState
287
320
288
321
return @ {
289
322
CimInstance = $cimInstance
@@ -318,24 +351,12 @@ class dscResourceObject {
318
351
[PSCustomObject ] $properties
319
352
}
320
353
321
-
322
-
323
- # $out = [dscResourceObject]@{
324
- # name = "root.cimv2/Win32_Environment"
325
- # type = "root.cimv2/Win32_Environment"
326
- # properties = [PSCustomObject]@{
327
- # Name = "test"
328
- # VariableValue = "TestValue"
329
- # UserName = ("{0}\{1}" -f $env:USERDOMAIN, $env:USERNAME) # Read-only property required
330
- # }
331
- # }
332
-
333
354
$out = [dscResourceObject ]@ {
334
- name = " root.cimv2/Win32_Environment"
335
- type = " root.cimv2/Win32_Environment"
355
+ name = ' root.cimv2/Win32_Environment'
356
+ type = ' root.cimv2/Win32_Environment'
336
357
properties = [PSCustomObject ]@ {
358
+ UserName = " {0}\{1}" -f $env: USERDOMAIN , $env: USERNAME
359
+ VariableValue = ' update'
337
360
Name = ' test'
338
- VariableValue = ' TestValue'
339
- UserName = (" {0}\{1}" -f $env: USERDOMAIN , $env: USERNAME ) # Read-only property required
340
361
}
341
362
}
0 commit comments