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

Improve the wmi beats module 42650 #42663

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f1f09e7
Add the Domain parameter. Remove reference to the IP address
herrBez Feb 10, 2025
64d5706
Merge branch 'main' of github.com:elastic/beats into improve-the-wmi-…
herrBez Feb 10, 2025
30a5f45
Add the domain to the wmi data
herrBez Feb 10, 2025
1186460
Improve the documentation of the WMI module
herrBez Feb 10, 2025
5eff223
Update metricbeat/module/windows/wmi/_meta/docs.asciidoc
herrBez Feb 12, 2025
461c09a
Update metricbeat/module/windows/wmi/_meta/docs.asciidoc
herrBez Feb 12, 2025
1cf6ef4
Add wmi.namespace and rewrite the Configuration section
herrBez Feb 17, 2025
77ab16b
Add section about best-practice and about Arbitrator
herrBez Feb 17, 2025
d8f479d
Improve phrasing of the best practices section
herrBez Feb 18, 2025
09a0c2e
Fix the convertDateTime function and add a test for the dates with ti…
herrBez Feb 18, 2025
1fe7f2e
Refactor replace fields with properties to be compliant with WMI docu…
herrBez Feb 18, 2025
4ed5563
Refactor the wmi.include_null and wmi.include_empty_string to a more …
herrBez Feb 18, 2025
afbced1
Run make update
herrBez Feb 18, 2025
c35c1e7
Make the warning more accurate
herrBez Feb 18, 2025
0974350
Fix typo
herrBez Feb 19, 2025
43af227
Fix linting error
herrBez Feb 19, 2025
e05a887
fix the example of the documentation + reorder the parameter definiti…
herrBez Feb 19, 2025
2c23092
Merge branch 'main' into improve-the-wmi-beats-module-42650
herrBez Feb 19, 2025
8249b47
remove reference to fields that should have been removed with the ref…
herrBez Feb 19, 2025
ac2f9a9
fix typos and make sure the period is 10 minutes in our examples
herrBez Feb 19, 2025
d2ecd8b
Fix the name of the test
herrBez Feb 19, 2025
3ce31bd
Hide the Remote WMI parameters
herrBez Feb 19, 2025
a779965
Merge branch 'main' into improve-the-wmi-beats-module-42650
ishleenk17 Mar 3, 2025
28ed9a7
Remove remote wmi logic from wmi.go. This can be reactivated by need
herrBez Mar 4, 2025
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
20 changes: 12 additions & 8 deletions metricbeat/docs/modules/windows.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,28 @@ metricbeat.modules:

- module: windows
metricsets: ["wmi"]
period: 60s
period: 10m
wmi:
include_null: false # Exclude fields with null values from the output
include_queries: false # Do not include the query string in the output
include_empty_string: false # Exclude fields with empty string values from the output
warning_threshold: 30s # Maximum time to wait for a query result before logging a warning (defaults to period)
# Do not include the query string in the output
include_queries: false
# Exclude properties with null values from the output
include_null_properties: false
# Exclude properties with empty string values from the output
include_empty_string_properties: false
# Maximum time to wait for a query result before logging a warning (defaults to period)
warning_threshold: 10m
# Default WMI namespace for all queries (if not specified per query)
# Uncomment to override the default, which is "root\\cimv2".
# namespace: "root\\cimv2"
queries:
- class: Win32_OperatingSystem # FROM: Class to fetch
fields: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- class: Win32_OperatingSystem # FROM: Class to fetch
properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- FreePhysicalMemory
- FreeSpaceInPagingFiles
- FreeVirtualMemory
- LocalDateTime
- NumberOfUsers
where: "" # Optional WHERE clause to filter query results
where: "" # Optional WHERE clause to filter query results
# Override the WMI namespace for this specific query (optional).
# If set, this takes precedence over the default namespace above.
# namespace: "root\\cimv2" # Overrides the metric
Expand Down
20 changes: 12 additions & 8 deletions metricbeat/metricbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1093,24 +1093,28 @@ metricbeat.modules:

- module: windows
metricsets: ["wmi"]
period: 60s
period: 10m
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you refresh mymind on why we are moving to 10m ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, we had an internal conversion with @strawgate about that manner. We want to basically discourage having too short periods because WMI may be pretty heavy on the system.

wmi:
include_null: false # Exclude fields with null values from the output
include_queries: false # Do not include the query string in the output
include_empty_string: false # Exclude fields with empty string values from the output
warning_threshold: 30s # Maximum time to wait for a query result before logging a warning (defaults to period)
# Do not include the query string in the output
include_queries: false
# Exclude properties with null values from the output
include_null_properties: false
# Exclude properties with empty string values from the output
include_empty_string_properties: false
# Maximum time to wait for a query result before logging a warning (defaults to period)
warning_threshold: 10m
# Default WMI namespace for all queries (if not specified per query)
# Uncomment to override the default, which is "root\\cimv2".
# namespace: "root\\cimv2"
queries:
- class: Win32_OperatingSystem # FROM: Class to fetch
fields: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- class: Win32_OperatingSystem # FROM: Class to fetch
properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- FreePhysicalMemory
- FreeSpaceInPagingFiles
- FreeVirtualMemory
- LocalDateTime
- NumberOfUsers
where: "" # Optional WHERE clause to filter query results
where: "" # Optional WHERE clause to filter query results
# Override the WMI namespace for this specific query (optional).
# If set, this takes precedence over the default namespace above.
# namespace: "root\\cimv2" # Overrides the metric
Expand Down
20 changes: 12 additions & 8 deletions metricbeat/module/windows/_meta/config.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,28 @@

- module: windows
metricsets: ["wmi"]
period: 60s
period: 10m
wmi:
include_null: false # Exclude fields with null values from the output
include_queries: false # Do not include the query string in the output
include_empty_string: false # Exclude fields with empty string values from the output
warning_threshold: 30s # Maximum time to wait for a query result before logging a warning (defaults to period)
# Do not include the query string in the output
include_queries: false
# Exclude properties with null values from the output
include_null_properties: false
# Exclude properties with empty string values from the output
include_empty_string_properties: false
# Maximum time to wait for a query result before logging a warning (defaults to period)
warning_threshold: 10m
# Default WMI namespace for all queries (if not specified per query)
# Uncomment to override the default, which is "root\\cimv2".
# namespace: "root\\cimv2"
queries:
- class: Win32_OperatingSystem # FROM: Class to fetch
fields: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- class: Win32_OperatingSystem # FROM: Class to fetch
properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
- FreePhysicalMemory
- FreeSpaceInPagingFiles
- FreeVirtualMemory
- LocalDateTime
- NumberOfUsers
where: "" # Optional WHERE clause to filter query results
where: "" # Optional WHERE clause to filter query results
# Override the WMI namespace for this specific query (optional).
# If set, this takes precedence over the default namespace above.
# namespace: "root\\cimv2" # Overrides the metric
6 changes: 3 additions & 3 deletions metricbeat/module/windows/_meta/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
# - module: windows
# metricsets:
# - wmi
# period: 60s
# period: 10m
# wmi:
# warning_threshold: 30s
# warning_threshold: 10m
# # namespace: "root\\cimv2"
# queries:
# - class: Win32_OperatingSystem # FROM: Class to fetch
# fields: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties
# properties: # SELECT: Properties to retrieve for this WMI class. Omit the setting to fetch all properties
# - FreePhysicalMemory
# - FreeSpaceInPagingFiles
# - FreeVirtualMemory
Expand Down
131 changes: 115 additions & 16 deletions metricbeat/module/windows/wmi/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
@@ -1,44 +1,143 @@
The `wmi` metricset of the Windows module reads metrics via Windows Management Instrumentation link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/about-wmi[(WMI)], a core management technology in the Windows Operating system.
The `wmi` metricset of the Windows module collects metrics via link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/about-wmi[Windows Management Instrumentation (WMI)], a core management technology in the Windows Operating system.

By leveraging WMI Query Language (WQL), this metricset allows you to extract detailed
system information and metrics to monitor the health and performance of Windows
Systems.

This metricset leverages the link:https://github.com/microsoft/wmi[Microsoft WMI], library a
convenient wrapper around the link:https://github.com/go-ole[GO-OLE] library which allows to
invoke the WMI Api.
This metricset leverages the link:https://github.com/microsoft/wmi[Microsoft WMI] library, a
convenient wrapper around the link:https://github.com/go-ole[Go-OLE] library. This allows invoking the
link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/scripting-api-for-wmi[Scripting API for WMI].

[float]
=== WMI Query Language (WQL) Support

This metricset supports the execution of link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/wql-sql-for-wmi[WQL] queries, a SQL-like query language for retrieving information from WMI namespaces.

As of now, we only support and execute queries with `SELECT`, `FROM` and `WHERE` clauses.
Currently, the metricset supports queries with `SELECT`, `FROM` and `WHERE` clauses.

NOTE: When working with WMI queries, it is the user's responsibility to ensure that queries are safe, efficient, and do not cause unintended side effects. A notorious example of a problematic WMI class is Win32_Product. Read more in link:https://support.microsoft.com/kb/974524[Windows Documentation].

[float]
[[wmi-arbitrator-and-query-execution]]
==== WMI Arbitrator and Query Execution

Query execution is managed by the underlying WMI Framework, specifically the link:https://learn.microsoft.com/en-us/troubleshoot/windows-server/system-management-components/new-wmi-arbitrator-behavior-in-windows-server[WMI Arbitrator].
The Arbitrator is responsible for:

- Scheduling and controlling query execution
- Throttling or stopping queries based on system resource availability and conditions

There is no way to directly stop a query once it has started. To prevent Metricbeat from waiting indefinitely for a query to return a result or fail, Metricbeat has a timeout mechanism that stops waiting for query results after a specified timeout. This is controlled by the `wmi.warning_threshold` setting.

NOTE: While Metricbeat stops waiting for the result, the underlying WMI query may continue running until the WMI Arbitrator decides to stop execution.


[float]
=== Configuration

[source,yaml]
----
- module: windows
metricsets: ["wmi"]
period: 60s
namespace: "root\\cimv2" # Namespace
queries:
- class: Win32_OperatingSystem
fields:
- FreePhysicalMemory
- FreeSpaceInPaginFiles
- NumberOfUsers
# Where Clasue
where: ""
period: 10m
wmi:
namespace: "root\\cimv2" # Default Namespace
warning_threshold: 10m
include_queries: true
include_null_properties: false
include_empty_strings_properties: false
queries:
- class: Win32_OperatingSystem
properties:
- FreePhysicalMemory
- FreeSpaceInPagingFiles
- NumberOfUsers
where: ""
- class: Win32_PowerPlan
properties: []
where: "IsActive = True"
namespace: "root\\cimv2\\power" # Overwrites the module namespace in this query
----

*`wmi.namespace`*::
The default WMI namespace used for queries. This can be overridden per query.
The default is `root\cimv2`.

*`wmi.warning_threshold`*:: The time threshold after which Metricbeat will stop
waiting for the query result and return control to the main flow of the program.
A warning is logged indicating that the query execution has exceeded the threshold.
The default is equal to the period. See <<wmi-arbitrator-and-query-execution, WMI Arbitrator and Query Execution>>
for more details.

*`wmi.include_queries`*:: If set to `true` the metricset includes the query in the output document. The default value is `false`.

*`wmi.include_null_properties`*:: If set to `true` the metricset includes the properties that have null value in the output document.
properties that have a `null` value in the output document. The default value is `false`.

*`wmi.include_empty_string_properties`*:: A boolean option that causes the metricset to include
the properties that are empty string. The default value is `false`.

*`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <<query-configuration, Query Configuration>> for the format of the queries.

[float]
[[query-configuration]]
==== Query Configuration

Each item in the `queries` list specifies a wmi query to perform.

*`class`*:: The wmi class. In the query it specifies the `FROM` clause. Required

*`properties`*:: List of properties to return. In the query it specifies the `SELECT` clause. Set it to the empty list (default value) to retrieve all available properties.

*`where`*:: The where clause. In the query it specifies the `WHERE` clause. Read more about the format link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/where-clause[in the Windows Documentation].

*`namespace`*:: The WMI Namespace for this particular query (it overwrites the metricset's `namespace` value)

[float]
===== Example

Example WQL Query:

[source,sql]
----
SELECT Name, ProcessId, WorkingSetSize
FROM Win32_Process
WHERE Name = 'lsass.exe' AND WorkingSetSize > 104857600
----

Equivalent YAML Configuration:

[source,yaml]
----
- class: Win32_Process
properties:
- Name
- ProcessId
- WorkingSetSize
where: "Name = 'lsass.exe' AND WorkingSetSize > 104857600"
----


[float]
=== Best Practices

- Test your queries in isolation using the `Get-CimInstance` PowerShell cmdlet or the WMI Explorer.

- Ensure that `wmi.warning_threshold` is **less than or equal to** the module's `period`.
This prevents starting intentionally multiple executions of the same query.

- Set up alerts in Metricbeat logs for timeouts and empty query results. If a query frequently times out or returns no data, investigate the cause to prevent missing critical information.

- [Advanced] Collect WMI-Activity Operational Logs to correlate with Metricbeat WMI warnings.


[float]
=== Compatibility

This module has been tested on the following platform:

- Operating System: Microsoft Windows Server 2019 Datacenter
- Architecture: x86
- Architecture: x64

Other Windows versions and architectures may also work but have not been explicitly tested.

49 changes: 27 additions & 22 deletions metricbeat/module/windows/wmi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,37 @@ import (
)

type Config struct {
IncludeQueries bool `config:"wmi.include_queries"` // Determines if the query string should be included in the output document
IncludeNull bool `config:"wmi.include_null"` // Specifies whether to include fields with nil values in the final document
IncludeEmptyString bool `config:"wmi.include_empty_string"` // Specifies whether to include fields with empty string values in the final document
Host string `config:"wmi.host"` // Hostname or IP address of the remote WMI server
User string `config:"wmi.username"` // Username for authentication on the remote WMI server
Password string `config:"wmi.password"` // Password for authentication on the remote WMI server
Namespace string `config:"wmi.namespace"` // Default WMI namespace for executing queries, used if not overridden by individual query configurations
Queries []QueryConfig `config:"wmi.queries"` // List of WMI query configurations
WarningThreshold time.Duration `config:"wmi.warning_threshold"` // Maximum duration to wait for query results before logging a warning. The query will continue running in WMI but will no longer be awaited
NamespaceQueryIndex map[string][]QueryConfig // Internal structure indexing queries by namespace to reduce the number of WMI connections required per execution
IncludeQueries bool `config:"wmi.include_queries"` // Determines if the query string should be included in the output document
IncludeNullProperties bool `config:"wmi.include_null_properties"` // Specifies whether to include properties with nil values in the final document
IncludeEmptyStringProperties bool `config:"wmi.include_empty_string_properties"` // Specifies whether to include properties with empty string values in the final document
Namespace string `config:"wmi.namespace"` // Default WMI namespace for executing queries, used if not overridden by individual query configurations
Queries []QueryConfig `config:"wmi.queries"` // List of WMI query configurations
WarningThreshold time.Duration `config:"wmi.warning_threshold"` // Maximum duration to wait for query results before logging a warning. The query will continue running in WMI but will no longer be awaited
NamespaceQueryIndex map[string][]QueryConfig // Internal structure indexing queries by namespace to reduce the number of WMI connections required per execution
// Remote WMI Parameters
// These parameters are intentionally hidden to discourage their use.
// If you need access, please open a support ticket to request exposure.
Host string // Hostname of the remote WMI server
Domain string // Domain of the remote WMI Server
User string // Username for authentication on the remote WMI server
Password string // Password for authentication on the remote WMI server
}

type QueryConfig struct {
QueryStr string // The compiled query string generated internally (not user-configurable)
Class string `config:"class"` // WMI class to query (used in the FROM clause)
Fields []string `config:"fields"` // List of properties to retrieve (used in the SELECT clause). If omitted, all properties of the class are fetched
Where string `config:"where"` // Custom WHERE clause to filter query results. The provided string is used directly in the query
Namespace string `config:"namespace"` // WMI namespace for the query. This takes precedence over the globally configured namespace
QueryStr string // The compiled query string generated internally (not user-configurable)
Class string `config:"class"` // WMI class to query (used in the FROM clause)
Properties []string `config:"properties"` // List of properties to retrieve (used in the SELECT clause). If omitted, all properties of the class are fetched
Where string `config:"where"` // Custom WHERE clause to filter query results. The provided string is used directly in the query
Namespace string `config:"namespace"` // WMI namespace for the query. This takes precedence over the globally configured namespace
}

func NewDefaultConfig() Config {
return Config{
IncludeQueries: false,
IncludeNull: false,
Host: "localhost",
Namespace: WMIDefaultNamespace,
IncludeQueries: false,
IncludeNullProperties: false,
IncludeEmptyStringProperties: false,
Host: "localhost",
Namespace: WMIDefaultNamespace,
}
}

Expand All @@ -72,11 +77,11 @@ func (c *Config) ValidateConnectionParameters() error {
func (qc *QueryConfig) compileQuery() {
// Let us normalize the case where the array is ['*']
// To the Empty Array
if len(qc.Fields) == 1 && qc.Fields[0] == "*" {
qc.Fields = []string{}
if len(qc.Properties) == 1 && qc.Properties[0] == "*" {
qc.Properties = []string{}
}

query := wmiquery.NewWmiQueryWithSelectList(qc.Class, qc.Fields, []string{}...)
query := wmiquery.NewWmiQueryWithSelectList(qc.Class, qc.Properties, []string{}...)
queryStr := query.String()
// Concatenating the where clause manually, because the library supports only a subset of where clauses
// while we want to leverage all filtering capabilities
Expand Down
Loading
Loading