Skip to content

Commit

Permalink
Merge pull request #548 from SteveL-MSFT/demo
Browse files Browse the repository at this point in the history
Update WMI adapter to perform query based on provided properties
  • Loading branch information
SteveL-MSFT authored Sep 17, 2024
2 parents ae330c2 + f2da909 commit 01bc057
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 26 deletions.
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
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 {
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"
}

0 comments on commit 01bc057

Please sign in to comment.