@@ -43,74 +43,108 @@ function GetValidCimProperties {
4343 [Parameter (Mandatory = $true , ValueFromPipeline = $true )]
4444 [Microsoft.Management.Infrastructure.CimClass ]$CimClass ,
4545
46+ [Parameter (Mandatory = $true )]
47+ $ClassName ,
48+
4649 [Parameter ()]
4750 [object ]$Properties ,
4851
4952 [Parameter ()]
50- [ValidateSet (' Get' , ' Set' , ' Test' )]
51- [string ]$Operation
53+ [switch ] $SkipReadOnly ,
54+
55+ [Parameter ()]
56+ [switch ] $ValidateKeyProperty
5257 )
5358
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
6782 }
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
8195 }
96+ }
97+ }
8298
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
110108 }
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
111112 }
112113 }
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+ # }
114148
115149 return $validatedProperties
116150}
@@ -120,11 +154,7 @@ function GetWmiInstance {
120154 param
121155 (
122156 [Parameter (Mandatory = $true , ValueFromPipeline = $true )]
123- [psobject ]$DesiredState ,
124-
125- [Parameter ()]
126- [ValidateSet (' Get' , ' Set' , ' Test' )]
127- [string ]$Operation = ' Get'
157+ [psobject ]$DesiredState
128158 )
129159
130160 $type_fields = $DesiredState.type -split " /"
@@ -134,36 +164,41 @@ function GetWmiInstance {
134164 $class = Get-CimClass - Namespace $wmi_namespace - ClassName $wmi_classname - ErrorAction Stop
135165
136166 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
138168
139169 $query = " SELECT $ ( $properties.Name -join ' ,' ) FROM $wmi_classname "
140170 $where = " WHERE "
141171 $useWhere = $false
142172 $first = $true
143173 foreach ($property in $properties ) {
144174 # 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 ) ) {
146176 $useWhere = $true
147177 if ($first ) {
148178 $first = $false
149179 } else {
150180 $where += " AND "
151181 }
152182
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 ) ) '"
155185 } else {
156- $where += " $ ( $property.Name ) = $ ( $property.Value ) "
186+ $where += " $ ( $property.Name ) = $ ( $DesiredState .properties . $ ( $ property.Name ) ) "
157187 }
158188 }
159189 }
160190 if ($useWhere ) {
161191 $query += $where
162192 }
163193 " 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
165195 } 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
167202 }
168203
169204 return $wmi_instances
@@ -194,12 +229,10 @@ function GetCimSpace {
194229
195230 switch ($Operation ) {
196231 ' Get' {
197- # TODO: identify key properties and add WHERE clause to the query
198232 $wmi_instances = GetWmiInstance - DesiredState $DesiredState
199233
200234 if ($wmi_instances ) {
201235 $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
203236 $wmi_instance = $wmi_instances [0 ] # for 'Get' we return just first matching instance; for 'export' we return all instances
204237 $wmi_instance.psobject.properties | ForEach-Object {
205238 if (($_.Name -ne " type" ) -and (-not $_.Name.StartsWith (" Cim" ))) {
@@ -225,14 +258,14 @@ function GetCimSpace {
225258
226259 $wmi_instance.Properties | ForEach-Object {
227260 if ($r.properties.psobject.properties.name -contains $_.Name ) {
228- $properties [$_.Name ] = $_ .Value
261+ $properties [$_.Name ] = $r .properties . $ ( $_ .Name )
229262 }
230263 }
231264
232- $readOnlyProperties = $wmi_instance.Properties | Where-Object - Property IsReadOnly -eq $true
265+ $readOnlyProperties = $wmi_instance.Properties | Where-Object - Property Flags -like " *ReadOnly* "
233266
234267 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
236269 } else {
237270 # When calling Set-CimInstance, the read-only properties needs to be filtered out
238271 if ($readOnlyProperties ) {
@@ -242,7 +275,7 @@ function GetCimSpace {
242275 }
243276 }
244277 }
245- $wmi_instance.CimInstance | Set-CimInstance - Property $properties - ErrorAction Stop
278+ $wmi_instance.CimInstance | Set-CimInstance - Property $properties - ErrorAction Ignore - ErrorVariable err | Out-Null
246279 }
247280
248281 $addToActualState = [dscResourceObject ]@ {
@@ -281,9 +314,9 @@ function ValidateCimMethodAndArguments {
281314 exit 1
282315 }
283316
284- $validatedProperties = GetValidCimProperties - CimClass $cimClass - Properties $DesiredState.properties - Operation Set
317+ $validatedProperties = GetValidCimProperties - CimClass $cimClass - ClassName $className - Properties $DesiredState.properties - ValidateKeyProperty
285318
286- $cimInstance = GetWmiInstance - DesiredState $DesiredState - Operation Set
319+ $cimInstance = GetWmiInstance - DesiredState $DesiredState
287320
288321 return @ {
289322 CimInstance = $cimInstance
@@ -318,24 +351,12 @@ class dscResourceObject {
318351 [PSCustomObject ] $properties
319352}
320353
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-
333354$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'
336357 properties = [PSCustomObject ]@ {
358+ UserName = " {0}\{1}" -f $env: USERDOMAIN , $env: USERNAME
359+ VariableValue = ' update'
337360 Name = ' test'
338- VariableValue = ' TestValue'
339- UserName = (" {0}\{1}" -f $env: USERDOMAIN , $env: USERNAME ) # Read-only property required
340361 }
341362}
0 commit comments