Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update WMI adapter to perform query based on provided properties #548

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 0 additions & 10 deletions dsc/examples/wmi.dsc.yaml

This file was deleted.

42 changes: 42 additions & 0 deletions dsc/examples/wmi_inventory.dsc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: WMI
type: Microsoft.Windows/WMI
properties:
resources:
- name: computer system
type: root.cimv2/Win32_ComputerSystem
properties:
name:
domain:
totalphysicalmemory:
model:
manufacturer:
- name: operating system
type: root.cimv2/Win32_OperatingSystem
properties:
caption:
version:
osarchitecture:
oslanguage:
- name: system enclosure
type: root.cimv2/Win32_SystemEnclosure
properties:
manufacturer:
model:
serialnumber:
- name: bios
type: root.cimv2/Win32_BIOS
properties:
manufacturer:
version:
serialnumber:
- name: network adapter
type: root.cimv2/Win32_NetworkAdapter
properties:
name:
macaddress:
adaptertype:
netconnectionid:
serviceName:
netconnectionstatus: 2
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is my understanding correct that this is the only instance in this file that will use WHERE clause?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes

4 changes: 4 additions & 0 deletions wmi-adapter/Tests/test_wmi_config.dsc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ resources:
resources:
- name: Get OS Info
type: root.cimv2/Win32_OperatingSystem
properties:
caption:
version:
osarchitecture:
- name: Get BIOS Info
type: root.cimv2/Win32_BIOS
- name: Get Processor Info
Expand Down
16 changes: 10 additions & 6 deletions wmi-adapter/Tests/wmi.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,22 @@ Describe 'WMI adapter resource tests' {
$r = Get-Content -Raw $configPath | dsc config get
$LASTEXITCODE | Should -Be 0
$res = $r | ConvertFrom-Json
$res.results[0].result.actualState[0].LastBootUpTime | Should -Not -BeNull
$res.results[0].result.actualState[1].BiosCharacteristics | Should -Not -BeNull
$res.results[0].result.actualState[2].NumberOfLogicalProcessors | Should -Not -BeNull
$res.results[0].result.actualState[0].LastBootUpTime | Should -BeNullOrEmpty
$res.results[0].result.actualState[0].Caption | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[0].Version | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[0].OSArchitecture | Should -Not -BeNullOrEmpty
}

It 'Example config works' -Skip:(!$IsWindows) {
$configPath = Join-Path $PSScriptRoot '..\..\dsc\examples\wmi.dsc.yaml'
$configPath = Join-Path $PSScriptRoot '..\..\dsc\examples\wmi_inventory.dsc.yaml'
$r = dsc config get -p $configPath
$LASTEXITCODE | Should -Be 0
$r | Should -Not -BeNullOrEmpty
$res = $r | ConvertFrom-Json
$res.results[0].result.actualState[0].Model | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[1].Description | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[0].Name | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[0].BootupState | Should -BeNullOrEmpty
$res.results[0].result.actualState[1].Caption | Should -Not -BeNullOrEmpty
$res.results[0].result.actualState[1].BuildNumber | Should -BeNullOrEmpty
$res.results[0].result.actualState[4].AdapterType | Should -BeLike "Ethernet*"
}
}
91 changes: 81 additions & 10 deletions wmi-adapter/wmi.resource.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,29 @@ param(
$stdinput
)

# catch any un-caught exception and write it to the error stream
trap {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be good to have a comment about what this trap does.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, will add comment

Write-Trace -Level Error -message $_.Exception.Message
exit 1
}

$ProgressPreference = 'Ignore'
$WarningPreference = 'Ignore'
$VerbosePreference = 'Ignore'

function Write-Trace {
param(
[string]$message,
[string]$level = 'Error'
)

$trace = [pscustomobject]@{
$level = $message
} | ConvertTo-Json -Compress

$host.ui.WriteErrorLine($trace)
}

function IsConfiguration($obj) {
if ($null -ne $obj.metadata -and $null -ne $obj.metadata.'Microsoft.DSC' -and $obj.metadata.'Microsoft.DSC'.context -eq 'Configuration') {
return $true
Expand All @@ -29,7 +48,6 @@ if ($Operation -eq 'List')
{
$version_string = "";
$author_string = "";
$moduleName = "";

$propertyList = @()
foreach ($p in $r.CimClassProperties)
Expand Down Expand Up @@ -79,17 +97,69 @@ elseif ($Operation -eq 'Get')
$wmi_namespace = $type_fields[0].Replace('.','\')
$wmi_classname = $type_fields[1]

#TODO: add filtering based on supplied properties of $r
$wmi_instances = Get-CimInstance -Namespace $wmi_namespace -ClassName $wmi_classname
# TODO: identify key properties and add WHERE clause to the query
if ($r.properties)
{
$query = "SELECT $($r.properties.psobject.properties.name -join ',') FROM $wmi_classname"
$where = " WHERE "
$useWhere = $false
$first = $true
foreach ($property in $r.properties.psobject.properties)
{
# TODO: validate property against the CIM class to give better error message
if ($null -ne $property.value)
{
$useWhere = $true
if ($first)
{
$first = $false
}
else
{
$where += " AND "
}

if ($property.TypeNameOfValue -eq "System.String")
{
$where += "$($property.Name) = '$($property.Value)'"
}
else
{
$where += "$($property.Name) = $($property.Value)"
}
}
}
if ($useWhere)
{
$query += $where
}
Write-Trace -Level Trace -message "Query: $query"
$wmi_instances = Get-CimInstance -Namespace $wmi_namespace -Query $query -ErrorAction Stop
}
else
{
$wmi_instances = Get-CimInstance -Namespace $wmi_namespace -ClassName $wmi_classname -ErrorAction Stop
}

if ($wmi_instances)
{
$instance_result = @{}
# TODO: for a `Get`, they key property must be provided so a specific instance is returned rather than just the first
$wmi_instance = $wmi_instances[0] # for 'Get' we return just first matching instance; for 'export' we return all instances
$wmi_instance.psobject.properties | %{
if (($_.Name -ne "type") -and (-not $_.Name.StartsWith("Cim")))
{
$instance_result[$_.Name] = $_.Value
if ($r.properties)
{
if ($r.properties.psobject.properties.name -contains $_.Name)
{
$instance_result[$_.Name] = $_.Value
}
}
else
{
$instance_result[$_.Name] = $_.Value
}
}
}

Expand All @@ -98,7 +168,7 @@ elseif ($Operation -eq 'Get')
else
{
$errmsg = "Can not find type " + $r.type + "; please ensure that Get-CimInstance returns this resource type"
Write-Error $errmsg
Write-Trace $errmsg
exit 1
}
}
Expand All @@ -110,11 +180,12 @@ elseif ($Operation -eq 'Get')
$wmi_classname = $type_fields[1]

#TODO: add filtering based on supplied properties of $inputobj_pscustomobj
$wmi_instances = Get-CimInstance -Namespace $wmi_namespace -ClassName $wmi_classname
$wmi_instances = Get-CimInstance -Namespace $wmi_namespace -ClassName $wmi_classname -ErrorAction Stop

if ($wmi_instances)
{
$wmi_instance = $wmi_instances[0] # for 'Get' we return just first matching instance; for 'export' we return all instances
# TODO: there's duplicate code here between configuration and non-configuration execution and should be refactored into a helper
$wmi_instance = $wmi_instances[0]
$result = @{}
$wmi_instance.psobject.properties | %{
if (($_.Name -ne "type") -and (-not $_.Name.StartsWith("Cim")))
Expand All @@ -126,7 +197,7 @@ elseif ($Operation -eq 'Get')
else
{
$errmsg = "Can not find type " + $inputobj_pscustomobj.type + "; please ensure that Get-CimInstance returns this resource type"
Write-Error $errmsg
Write-Trace $errmsg
exit 1
}
}
Expand All @@ -140,5 +211,5 @@ elseif ($Operation -eq 'Validate')
}
else
{
Write-Error "ERROR: Unsupported operation requested from wmigroup.resource.ps1"
}
Write-Trace "ERROR: Unsupported operation requested from wmigroup.resource.ps1"
}
Loading