From f1f09e70928b19e6a20f6043f75290227fbcbb0f Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Mon, 10 Feb 2025 16:48:30 +0100 Subject: [PATCH 01/21] Add the Domain parameter. Remove reference to the IP address --- metricbeat/module/windows/wmi/config.go | 3 ++- metricbeat/module/windows/wmi/wmi.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/metricbeat/module/windows/wmi/config.go b/metricbeat/module/windows/wmi/config.go index ad0e4d02df9a..dc5960ef426e 100644 --- a/metricbeat/module/windows/wmi/config.go +++ b/metricbeat/module/windows/wmi/config.go @@ -34,7 +34,8 @@ 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 + Host string `config:"wmi.host"` // Hostname of the remote WMI server + Domain string `config:"wmi.domain"` // Domain 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 diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index 26f9b8a30404..196464695979 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -113,7 +113,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { // for each unique WMI namespace. This minimizes the number of session creations for namespace, queries := range m.config.NamespaceQueryIndex { - session, err := sm.GetSession(namespace, m.config.Host, "", m.config.User, m.config.Password) + session, err := sm.GetSession(namespace, m.config.Host, m.config.Domain, m.config.User, m.config.Password) if err != nil { return fmt.Errorf("could not initialize session %w", err) From 30a5f457cb44e7c3d66fadd818ae9cfb98847b9f Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Mon, 10 Feb 2025 19:54:28 +0100 Subject: [PATCH 02/21] Add the domain to the wmi data --- metricbeat/module/windows/wmi/wmi.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index 196464695979..e990669cf78f 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -154,6 +154,10 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { }, } + if m.config.Domain != "" { + event.MetricSetFields.Put("domain", m.config.Domain) + } + if m.config.IncludeQueries { event.MetricSetFields.Put("query", query) } From 11864606af1f6149e7b080f96469cbb8df67460b Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Mon, 10 Feb 2025 20:05:20 +0100 Subject: [PATCH 03/21] Improve the documentation of the WMI module - Add [float] to the internal sections - Mention all parameters - Make sure the examples has two queries and one of them uses where and namespace - Show an example of how to convert an WQL query into a YAML Configuration --- .../module/windows/wmi/_meta/docs.asciidoc | 105 +++++++++++++++--- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 94dc73b67f0b..36c326988612 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -1,19 +1,21 @@ -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 collect 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]. 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. +[float] === Configuration [source,yaml] @@ -21,15 +23,92 @@ As of now, we only support and execute queries with `SELECT`, `FROM` and `WHERE` - module: windows metricsets: ["wmi"] period: 60s - namespace: "root\\cimv2" # Namespace - queries: - - class: Win32_OperatingSystem - fields: - - FreePhysicalMemory - - FreeSpaceInPaginFiles - - NumberOfUsers - # Where Clasue - where: "" + wmi: + # host: localhost + # domain: "" + # username: test + # password: foo + namespace: "root\\cimv2" # Default Namespace + warning_threshold: 60s + include_queries: true + include_null: false + include_empty_strings: false + queries: + - class: Win32_OperatingSystem + fields: + - FreePhysicalMemory + - FreeSpaceInPagingFiles + - NumberOfUsers + # Where Clasue + where: "" + - class: Win32_PowerPlan + fields: [] + where: "IsActive = True" + namespace: "root\\cimv2\\power" # Overwrites the module namespace in this query +---- + + +*`wmi.host`*:: The host for the remote WMI Connection. By default is localhost. + +*`wmi.domain`*:: The domain for the remote WMI Connection. By default is the empty string. + +*`wmi.username`*:: The username for the remote WMI Connection. By default is empty string. + +*`wmi.password`*:: The password for the remote WMI Connection. By default is empty string. + +*`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. +By default is equal to the period. + +*`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <> for the format of the queries. + +*`wmi.include_queries`*:: A boolean option that causes the metricset to include the +query in the output document. False by default. + +*`wmi.include_null`*:: A boolean option that causes the metricset to include the +properties that have a null value in the output document. False by default. + +*`wmi.include_empty_string`*:: A boolean option that causes the metricset to include +the properties that are empty string. False by default. + + +[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 + +*`fields`*:: List of fields 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 https://learn.microsoft.com/en-us/windows/win32/wmisdk/where-clause. + +*`namespace`*:: The WMI Namespace for this particular query (it overwrites the module'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 + fields: + - Name + - ProcessId + - WorkingSetSize + where: "Name = 'lsass.exe' AND WorkingSetSize > 104857600" ---- [float] From 5eff2230b2472b148e41dec8a62912fa8530e99b Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 12 Feb 2025 14:58:20 +0100 Subject: [PATCH 04/21] Update metricbeat/module/windows/wmi/_meta/docs.asciidoc Co-authored-by: Ishleen Kaur <102962586+ishleenk17@users.noreply.github.com> --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 36c326988612..f6af0cbdcb68 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -63,7 +63,7 @@ By default is equal to the period. *`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <> for the format of the queries. -*`wmi.include_queries`*:: A boolean option that causes the metricset to include the +*`wmi.include_queries`*:: If set to true the metricset includes the query in the output document. query in the output document. False by default. *`wmi.include_null`*:: A boolean option that causes the metricset to include the From 461c09a75b5c62f497e523e1cd15bd087107599b Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 12 Feb 2025 14:58:38 +0100 Subject: [PATCH 05/21] Update metricbeat/module/windows/wmi/_meta/docs.asciidoc Co-authored-by: Ishleen Kaur <102962586+ishleenk17@users.noreply.github.com> --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index f6af0cbdcb68..bd9937c823b3 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -66,7 +66,7 @@ By default is equal to the period. *`wmi.include_queries`*:: If set to true the metricset includes the query in the output document. query in the output document. False by default. -*`wmi.include_null`*:: A boolean option that causes the metricset to include the +*`wmi.include_null`*:: 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. False by default. *`wmi.include_empty_string`*:: A boolean option that causes the metricset to include From 1cf6ef40f5b10ac77cce48d417713f51a85a115c Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Mon, 17 Feb 2025 08:29:34 +0100 Subject: [PATCH 06/21] Add wmi.namespace and rewrite the Configuration section --- .../module/windows/wmi/_meta/docs.asciidoc | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index bd9937c823b3..60cae9022894 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -47,30 +47,32 @@ As of now, we only support and execute queries with `SELECT`, `FROM` and `WHERE` 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.host`*:: The host for the remote WMI Connection. By default is localhost. +*`wmi.host`*:: The host for the remote WMI Connection. The default is `localhost`. -*`wmi.domain`*:: The domain for the remote WMI Connection. By default is the empty string. +*`wmi.domain`*:: The domain for the remote WMI Connection. The default is the empty string (`""`). -*`wmi.username`*:: The username for the remote WMI Connection. By default is empty string. +*`wmi.username`*:: The username for the remote WMI Connection. The default is empty string (`""`). -*`wmi.password`*:: The password for the remote WMI Connection. By default is empty string. +*`wmi.password`*:: The password for the remote WMI Connection. The default is empty string (`""`). *`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. -By default is equal to the period. +The default is equal to the period. *`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <> for the format of the queries. -*`wmi.include_queries`*:: If set to true the metricset includes the query in the output document. -query in the output document. False by default. +*`wmi.include_queries`*:: If set to `true` the metricset includes the query in the output document. The default value is `false`. -*`wmi.include_null`*:: 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. False by default. +*`wmi.include_null`*:: 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`*:: A boolean option that causes the metricset to include -the properties that are empty string. False by default. +the properties that are empty string. The default value is `false`. [float] From 77ab16bb30dcd708cf9cc9577d728305e7c9d7ca Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Mon, 17 Feb 2025 11:20:31 +0100 Subject: [PATCH 07/21] Add section about best-practice and about Arbitrator --- .../module/windows/wmi/_meta/docs.asciidoc | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 60cae9022894..8ee822b97dc9 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -5,7 +5,7 @@ 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]. This allows invoking the +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] @@ -13,7 +13,24 @@ link:https://learn.microsoft.com/en-us/windows/win32/wmisdk/scripting-api-for-wm 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 @@ -53,16 +70,17 @@ The default is `root\cimv2`. *`wmi.host`*:: The host for the remote WMI Connection. The default is `localhost`. -*`wmi.domain`*:: The domain for the remote WMI Connection. The default is the empty string (`""`). +*`wmi.domain`*:: The domain for the remote WMI Connection. The default is an empty string (`""`). -*`wmi.username`*:: The username for the remote WMI Connection. The default is empty string (`""`). +*`wmi.username`*:: The username for the remote WMI Connection. The default is an empty string (`""`). -*`wmi.password`*:: The password for the remote WMI Connection. The default is empty string (`""`). +*`wmi.password`*:: The password for the remote WMI Connection. The default is an empty string (`""`). *`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. +The default is equal to the period. See <> +for more details. *`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <> for the format of the queries. @@ -85,9 +103,9 @@ Each item in the `queries` list specifies a wmi query to perform. *`fields`*:: List of fields 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 https://learn.microsoft.com/en-us/windows/win32/wmisdk/where-clause. +*`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 module's `namespace` value) +*`namespace`*:: The WMI Namespace for this particular query (it overwrites the metricset's `namespace` value) [float] ===== Example @@ -113,6 +131,21 @@ Equivalent YAML Configuration: where: "Name = 'lsass.exe' AND WorkingSetSize > 104857600" ---- + +[float] +=== Best Practices + +- Test your queries in isolation with the command `metricbeat test modules` + +- Ensure that `wmi.warning_threshold` is **less than or equal to** the module's `period`. + This prevents starting 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 avoid missing critical information. + +- [Advanced] Collect WMI-Activity Operational Logs to correlate Metricbeat wmi warnings. + + [float] === Compatibility From d8f479d7d12f6a061c613fa2db6e750023a85904 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Tue, 18 Feb 2025 14:57:31 +0100 Subject: [PATCH 08/21] Improve phrasing of the best practices section --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 8ee822b97dc9..58218fe1ee5d 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -135,15 +135,14 @@ Equivalent YAML Configuration: [float] === Best Practices -- Test your queries in isolation with the command `metricbeat test modules` +- Test your queries in isolation using the `Get-CimInstance` powerhsell cmdlet or the WMI Explorer. - Ensure that `wmi.warning_threshold` is **less than or equal to** the module's `period`. - This prevents starting multiple executions of the same query. + 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 avoid missing critical information. +- 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 Metricbeat wmi warnings. +- [Advanced] Collect WMI-Activity Operational Logs to correlate with Metricbeat WMI warnings. [float] From 09a0c2ef3bc9dd27f9a890313bebd073e008feac Mon Sep 17 00:00:00 2001 From: herrBez Date: Tue, 18 Feb 2025 16:12:08 +0000 Subject: [PATCH 09/21] Fix the convertDateTime function and add a test for the dates with timezone --- metricbeat/module/windows/wmi/utils.go | 49 ++++++++++++++++++++- metricbeat/module/windows/wmi/utils_test.go | 14 +++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/metricbeat/module/windows/wmi/utils.go b/metricbeat/module/windows/wmi/utils.go index e43f59567b67..7bb53f1b3de0 100644 --- a/metricbeat/module/windows/wmi/utils.go +++ b/metricbeat/module/windows/wmi/utils.go @@ -48,9 +48,54 @@ func ConvertSint64(v string) (interface{}, error) { return strconv.ParseInt(v, 10, 64) } +const WMI_DATETIME_LAYOUT string = "20060102150405.999999" +const TIMEZONE_LAYOUT string = "-07:00" + +// The CIMDateFormat is defined as "yyyymmddHHMMSS.mmmmmmsUUU". +// Example: "20231224093045.123456+000" +// More information: https://learn.microsoft.com/en-us/windows/win32/wmisdk/cim-datetime +// +// The "yyyyMMddHHmmSS.mmmmmm" part can be parsed using time.Parse, but Go's time package does not support parsing the "sUUU" +// part (the sign and minute offset from UTC). +// +// Here, "s" represents the sign (+ or -), and "UUU" represents the UTC offset in minutes. +// +// The approach for handling this is: +// 1. Extract the sign ('+' or '-') from the string. +// 2. Normalize the offset from minutes to the standard `hh:mm` format. +// 3. Concatenate the "yyyyMMddHHmmSS.mmmmmm" part with the normalized offset. +// 4. Parse the combined string using time.Parse to return a time.Date object. func ConvertDatetime(v string) (interface{}, error) { - layout := "20060102150405.999999-0700" - return time.Parse(layout, v+"0") + if len(v) != 25 { + return nil, fmt.Errorf("datetime is invalid: the datetime is expected to be exactly 25 characters long, got: %s", v) + } + + // Extract the sign (either '+' or '-') + utcOffsetSign := v[21] + if utcOffsetSign != '+' && utcOffsetSign != '-' { + return nil, fmt.Errorf("datetime is invalid: the offset sign is expected to be either + or -") + } + + // Extract UTC offset (last 3 characters) + utcOffsetStr := v[22:] + utcOffset, err := strconv.ParseInt(utcOffsetStr, 10, 16) + if err != nil { + return nil, fmt.Errorf("datetime is invalid: error parsing UTC offset: %v", err) + } + offsetHours := utcOffset / 60 + offsetMinutes := utcOffset % 60 + + // Build the complete date string including the UTC offset in the format yyyyMMddHHmmss.mmmmmm+hh:mm + // Concatenate the date string with the offset formatted as "+hh:mm" + dateString := fmt.Sprintf("%s%c%02d:%02d", v[:21], utcOffsetSign, offsetHours, offsetMinutes) + + // Parse the combined datetime string using the defined layout + date, err := time.Parse(WMI_DATETIME_LAYOUT+TIMEZONE_LAYOUT, dateString) + if err != nil { + return nil, fmt.Errorf("datetime is invalid: error parsing the final datetime: %v", err) + } + + return date, err } func ConvertString(v string) (interface{}, error) { diff --git a/metricbeat/module/windows/wmi/utils_test.go b/metricbeat/module/windows/wmi/utils_test.go index 54f66e4d90a3..fb1e735c1262 100644 --- a/metricbeat/module/windows/wmi/utils_test.go +++ b/metricbeat/module/windows/wmi/utils_test.go @@ -121,6 +121,8 @@ func Test_RequiresExtraConversion(t *testing.T) { } } +const TEST_DATE_FORMAT string = "2006-01-02T15:04:05.999999-07:00" + func Test_ConversionFunctions(t *testing.T) { tests := []struct { name string @@ -170,8 +172,16 @@ func Test_ConversionFunctions(t *testing.T) { { name: "ConvertDatetime - valid input", conversion: ConvertDatetime, - input: "20231224093045.123456-000", - expected: mustParseTime("20060102150405.999999-0700", "20231224093045.123456-0000"), + input: "20231224093045.123456+000", + expected: mustParseTime(TEST_DATE_FORMAT, "2023-12-24T09:30:45.123456+00:00"), + expectErr: false, + description: "Should convert string to time.Time", + }, + { + name: "ConvertDatetime - valid input - timezone set", + conversion: ConvertDatetime, + input: "20231224093045.123456-690", + expected: mustParseTime(TEST_DATE_FORMAT, "2023-12-24T09:30:45.123456-11:30"), expectErr: false, description: "Should convert string to time.Time", }, From 1fe7f2e3d9b308c039d25a80211b3318d59f7c43 Mon Sep 17 00:00:00 2001 From: herrBez Date: Tue, 18 Feb 2025 16:43:31 +0000 Subject: [PATCH 10/21] Refactor replace fields with properties to be compliant with WMI documentation --- .../module/windows/_meta/config.reference.yml | 6 +- metricbeat/module/windows/_meta/config.yml | 2 +- .../module/windows/wmi/_meta/docs.asciidoc | 8 +- metricbeat/module/windows/wmi/config.go | 20 ++--- metricbeat/module/windows/wmi/config_test.go | 60 +++++++-------- metricbeat/module/windows/wmi/utils.go | 8 +- metricbeat/module/windows/wmi/utils_test.go | 74 +++++++++---------- metricbeat/module/windows/wmi/wmi.go | 42 +++++------ metricbeat/module/windows/wmi/wmi_test.go | 26 +++---- 9 files changed, 123 insertions(+), 123 deletions(-) diff --git a/metricbeat/module/windows/_meta/config.reference.yml b/metricbeat/module/windows/_meta/config.reference.yml index d033f457c48f..d7a2528a143d 100644 --- a/metricbeat/module/windows/_meta/config.reference.yml +++ b/metricbeat/module/windows/_meta/config.reference.yml @@ -22,16 +22,16 @@ metricsets: ["wmi"] period: 60s wmi: - include_null: false # Exclude fields with null values from the output + include_null: false # Exclude properties 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 + include_empty_string: false # Exclude properties 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) # 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 + properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties - FreePhysicalMemory - FreeSpaceInPagingFiles - FreeVirtualMemory diff --git a/metricbeat/module/windows/_meta/config.yml b/metricbeat/module/windows/_meta/config.yml index 45a9a709d725..3a0770946907 100644 --- a/metricbeat/module/windows/_meta/config.yml +++ b/metricbeat/module/windows/_meta/config.yml @@ -25,7 +25,7 @@ # # 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 diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 58218fe1ee5d..eaa448cdd85c 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -52,14 +52,14 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma include_empty_strings: false queries: - class: Win32_OperatingSystem - fields: + properties: - FreePhysicalMemory - FreeSpaceInPagingFiles - NumberOfUsers # Where Clasue where: "" - class: Win32_PowerPlan - fields: [] + properties: [] where: "IsActive = True" namespace: "root\\cimv2\\power" # Overwrites the module namespace in this query ---- @@ -101,7 +101,7 @@ 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 -*`fields`*:: List of fields to return. In the query it specifies the `SELECT` clause. Set it to the empty list (default value) to retrieve all available properties. +*`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]. @@ -124,7 +124,7 @@ Equivalent YAML Configuration: [source,yaml] ---- - class: Win32_Process - fields: + properties: - Name - ProcessId - WorkingSetSize diff --git a/metricbeat/module/windows/wmi/config.go b/metricbeat/module/windows/wmi/config.go index dc5960ef426e..d13338dff7be 100644 --- a/metricbeat/module/windows/wmi/config.go +++ b/metricbeat/module/windows/wmi/config.go @@ -32,8 +32,8 @@ 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 + IncludeNull bool `config:"wmi.include_null"` // Specifies whether to include properties with nil values in the final document + IncludeEmptyString bool `config:"wmi.include_empty_string"` // Specifies whether to include properties with empty string values in the final document Host string `config:"wmi.host"` // Hostname of the remote WMI server Domain string `config:"wmi.domain"` // Domain of the remote WMI Server User string `config:"wmi.username"` // Username for authentication on the remote WMI server @@ -45,11 +45,11 @@ type Config struct { } 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 { @@ -73,11 +73,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 diff --git a/metricbeat/module/windows/wmi/config_test.go b/metricbeat/module/windows/wmi/config_test.go index 443e05e5e35e..666e01b7afd5 100644 --- a/metricbeat/module/windows/wmi/config_test.go +++ b/metricbeat/module/windows/wmi/config_test.go @@ -88,9 +88,9 @@ func TestCompileQueries(t *testing.T) { config: Config{ Queries: []QueryConfig{ { - Class: "Win32_Process", - Fields: []string{"Name", "ID"}, - Where: "Name LIKE 'chrome%'", + Class: "Win32_Process", + Properties: []string{"Name", "ID"}, + Where: "Name LIKE 'chrome%'", }, }, }, @@ -119,50 +119,50 @@ func TestCompileQueries(t *testing.T) { // TestQueryCompile ensures individual query compilation works correctly. func TestQueryCompile(t *testing.T) { tests := []struct { - name string - queryConfig QueryConfig - expectedQuery string - expectedFieldsLength int + name string + queryConfig QueryConfig + expectedQuery string + expectedPropertiesLength int }{ { name: "Simple query with WHERE clause", queryConfig: QueryConfig{ - Class: "Win32_Process", - Fields: []string{"Name", "ProcessId"}, - Where: "Name = 'notepad.exe'", + Class: "Win32_Process", + Properties: []string{"Name", "ProcessId"}, + Where: "Name = 'notepad.exe'", }, - expectedQuery: "SELECT Name,ProcessId FROM Win32_Process WHERE Name = 'notepad.exe'", - expectedFieldsLength: 2, + expectedQuery: "SELECT Name,ProcessId FROM Win32_Process WHERE Name = 'notepad.exe'", + expectedPropertiesLength: 2, }, { - name: "Query with multiple fields and no WHERE clause", + name: "Query with multiple properties and no WHERE clause", queryConfig: QueryConfig{ - Class: "Win32_Service", - Fields: []string{"Name", "State", "StartMode"}, - Where: "", + Class: "Win32_Service", + Properties: []string{"Name", "State", "StartMode"}, + Where: "", }, - expectedQuery: "SELECT Name,State,StartMode FROM Win32_Service", - expectedFieldsLength: 3, + expectedQuery: "SELECT Name,State,StartMode FROM Win32_Service", + expectedPropertiesLength: 3, }, { - name: "Query with wildcard (*) for fields", + name: "Query with wildcard (*) for properties", queryConfig: QueryConfig{ - Class: "Win32_ComputerSystem", - Fields: []string{}, - Where: "Manufacturer = 'Dell'", + Class: "Win32_ComputerSystem", + Properties: []string{}, + Where: "Manufacturer = 'Dell'", }, - expectedQuery: "SELECT * FROM Win32_ComputerSystem WHERE Manufacturer = 'Dell'", - expectedFieldsLength: 0, + expectedQuery: "SELECT * FROM Win32_ComputerSystem WHERE Manufacturer = 'Dell'", + expectedPropertiesLength: 0, }, { name: "Query with wildcard (*) and no WHERE clause", queryConfig: QueryConfig{ - Class: "Win32_BIOS", - Fields: []string{"*"}, - Where: "", + Class: "Win32_BIOS", + Properties: []string{"*"}, + Where: "", }, - expectedQuery: "SELECT * FROM Win32_BIOS", - expectedFieldsLength: 0, // The normalization process make sure that ['*'] and [] are the same case + expectedQuery: "SELECT * FROM Win32_BIOS", + expectedPropertiesLength: 0, // The normalization process make sure that ['*'] and [] are the same case }, } @@ -170,7 +170,7 @@ func TestQueryCompile(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.queryConfig.compileQuery() assert.Equal(t, tt.expectedQuery, tt.queryConfig.QueryStr, "QueryStr should match the expected query string") - assert.Equal(t, tt.expectedFieldsLength, len(tt.queryConfig.Fields)) + assert.Equal(t, tt.expectedPropertiesLength, len(tt.queryConfig.Properties)) }) } } diff --git a/metricbeat/module/windows/wmi/utils.go b/metricbeat/module/windows/wmi/utils.go index 7bb53f1b3de0..cdf05202590a 100644 --- a/metricbeat/module/windows/wmi/utils.go +++ b/metricbeat/module/windows/wmi/utils.go @@ -104,8 +104,8 @@ func ConvertString(v string) (interface{}, error) { // Function that determines if a given value requires additional conversion // This holds true for strings that encode uint64, sint64 and datetime format -func RequiresExtraConversion(fieldValue interface{}) bool { - stringValue, isString := fieldValue.(string) +func RequiresExtraConversion(propertyValue interface{}) bool { + stringValue, isString := propertyValue.(string) if !isString { return false } @@ -130,11 +130,11 @@ func getPropertyType(property *ole.IDispatch) (base.WmiType, error) { return base.WmiType(value.(int32)), nil } -// Returns the "raw" SWbemProperty containing type information for a given field. +// Returns the "raw" SWbemProperty containing type information for a given property. // // The microsoft/wmi library does not have a function that given an instance and a property name // returns the wmi.wmiProperty object. This function mimics the behavior of the `GetSystemProperty` -// method in the wmi.wmiInstance struct and applies it on the Properties_ field +// method in the wmi.wmiInstance struct and applies it on the Properties_ property // https://github.com/microsoft/wmi/blob/v0.25.2/pkg/wmiinstance/WmiInstance.go#L87 // // Note: We are not instantiating a wmi.wmiProperty because of this issue diff --git a/metricbeat/module/windows/wmi/utils_test.go b/metricbeat/module/windows/wmi/utils_test.go index fb1e735c1262..281708475c93 100644 --- a/metricbeat/module/windows/wmi/utils_test.go +++ b/metricbeat/module/windows/wmi/utils_test.go @@ -58,64 +58,64 @@ func TestExecuteGuardedQueryInstances(t *testing.T) { func Test_RequiresExtraConversion(t *testing.T) { tests := []struct { - name string - fieldValue interface{} - expected bool - description string + name string + propertyValue interface{} + expected bool + description string }{ { - name: "Valid numeric string - ends with a digit", - fieldValue: "12345", - expected: true, - description: "Should require conversion as the string ends with a digit", + name: "Valid numeric string - ends with a digit", + propertyValue: "12345", + expected: true, + description: "Should require conversion as the string ends with a digit", }, { - name: "Empty string", - fieldValue: "", - expected: false, - description: "Should not require conversion as the string is empty", + name: "Empty string", + propertyValue: "", + expected: false, + description: "Should not require conversion as the string is empty", }, { - name: "Non-numeric string - no digits", - fieldValue: "abcdef", - expected: false, - description: "Should not require conversion as the string does not end with a digit", + name: "Non-numeric string - no digits", + propertyValue: "abcdef", + expected: false, + description: "Should not require conversion as the string does not end with a digit", }, { - name: "Mixed string - ends with a digit. Let us fetch the type", - fieldValue: "abc123", - expected: true, - description: "Should require conversion as the string ends with a digit", + name: "Mixed string - ends with a digit. Let us fetch the type", + propertyValue: "abc123", + expected: true, + description: "Should require conversion as the string ends with a digit", }, { - name: "String ending with a non-digit", - fieldValue: "123abc", - expected: false, - description: "Should not require conversion as the string ends with a non-digit", + name: "String ending with a non-digit", + propertyValue: "123abc", + expected: false, + description: "Should not require conversion as the string ends with a non-digit", }, { - name: "Nil input", - fieldValue: nil, - expected: false, - description: "Should not require conversion as the input is nil", + name: "Nil input", + propertyValue: nil, + expected: false, + description: "Should not require conversion as the input is nil", }, { - name: "Non-string input", - fieldValue: 12345, - expected: false, - description: "Should not require conversion as the input is not a string", + name: "Non-string input", + propertyValue: 12345, + expected: false, + description: "Should not require conversion as the input is not a string", }, { - name: "Datetime input - requires a conversion", - fieldValue: "20240925192747.000000+000", - expected: true, - description: "Should not require conversion as the input is not a string", + name: "Datetime input - requires a conversion", + propertyValue: "20240925192747.000000+000", + expected: true, + description: "Should not require conversion as the input is not a string", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := RequiresExtraConversion(tt.fieldValue) + result := RequiresExtraConversion(tt.propertyValue) assert.Equal(t, tt.expected, result, tt.description) }) } diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index e990669cf78f..801aa8fa1069 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -88,12 +88,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } // This function handles the skip conditions -func (m *MetricSet) shouldSkipNilOrEmptyValue(fieldValue interface{}) bool { - if fieldValue == nil { +func (m *MetricSet) shouldSkipNilOrEmptyValue(propertyValue interface{}) bool { + if propertyValue == nil { if !m.config.IncludeNull { return true // Skip if it's nil and IncludeNull is false } - } else if stringValue, ok := fieldValue.(string); ok { + } else if stringValue, ok := propertyValue.(string); ok { if len(stringValue) == 0 && !m.config.IncludeEmptyString { return true // Skip if it's an empty string and IncludeEmptyString is false } @@ -140,7 +140,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { } if len(rows) == 0 { - m.Logger().Warnf("The query '%s' did not produce results. This can be expected, but it can also be the result of querying an invalid field. Make sure all required fields do exist or check the WMI-Activity Operational Log for more information.", query) + m.Logger().Warnf("The query '%s' did not produce results. This can be expected, but it can also be the result of querying an invalid property. Make sure all required properties do exist or check the WMI-Activity Operational Log for more information.", query) } defer wmi.CloseAllInstances(rows) @@ -163,26 +163,26 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { } // Get only the required properties - properties := queryConfig.Fields + properties := queryConfig.Properties - // If the Fields array is empty we retrieve all fields - if len(queryConfig.Fields) == 0 { + // If the Properties array is empty we retrieve all properties available in the class + if len(queryConfig.Properties) == 0 { properties = instance.GetClass().GetPropertiesNames() } - for _, fieldName := range properties { - fieldValue, err := instance.GetProperty(fieldName) + for _, propertyName := range properties { + propertyValue, err := instance.GetProperty(propertyName) if err != nil { m.Logger().Error("Unable to get propery by name: %v", err) continue } - if m.shouldSkipNilOrEmptyValue(fieldValue) { + if m.shouldSkipNilOrEmptyValue(propertyValue) { continue } // The default case, we simply return what we got - finalValue := fieldValue + finalValue := propertyValue // The script API of WMI returns strings for uint64, sint64, datetime // Link: https://learn.microsoft.com/en-us/windows/win32/wmisdk/querying-wmi @@ -190,31 +190,31 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { // // Example: in the query: SELECT * FROM Win32_OperatingSystem // FreePhysicalMemory is a string, but it should be an uint64 - if RequiresExtraConversion(fieldValue) { - convertFun, ok := conversionTable[fieldName] + if RequiresExtraConversion(propertyValue) { + convertFun, ok := conversionTable[propertyName] // If the function is not found let us fetch it and cache it if !ok { - convertFun, err = GetConvertFunction(instance, fieldName, m.Logger()) + convertFun, err = GetConvertFunction(instance, propertyName, m.Logger()) if err != nil { - m.Logger().Warn("Skipping addition of field %s: Unable to retrieve the conversion function: %v", fieldName, err) + m.Logger().Warn("Skipping addition of property %s: Unable to retrieve the conversion function: %v", propertyName, err) continue } - conversionTable[fieldName] = convertFun + conversionTable[propertyName] = convertFun } // Perform the conversion at this point it's safe to cast to string. - fieldValueString, ok := fieldValue.(string) + propertyValueString, ok := propertyValue.(string) if !ok { - m.Logger().Warn("Skipping addition of field %s: expected a string found %v", fieldName, fieldValue) + m.Logger().Warn("Skipping addition of property %s: expected a string found %v", propertyName, propertyValue) continue } - convertedValue, err := convertFun(fieldValueString) + convertedValue, err := convertFun(propertyValueString) if err != nil { - m.Logger().Warn("Skipping addition of field %s. Cannot convert: %v", fieldName, err) + m.Logger().Warn("Skipping addition of property %s. Cannot convert: %v", propertyName, err) continue } finalValue = convertedValue } - event.MetricSetFields.Put(fieldName, finalValue) + event.MetricSetFields.Put(propertyName, finalValue) } report.Event(event) } diff --git a/metricbeat/module/windows/wmi/wmi_test.go b/metricbeat/module/windows/wmi/wmi_test.go index e9cb2e35be2e..05345edf0e64 100644 --- a/metricbeat/module/windows/wmi/wmi_test.go +++ b/metricbeat/module/windows/wmi/wmi_test.go @@ -28,51 +28,51 @@ import ( func TestShouldSkipNilOrEmptyValue(t *testing.T) { tests := []struct { key string - fieldValue interface{} + propertyValue interface{} includeNull bool includeEmptyString bool expectedShouldSkip bool }{ - // Test Case 1: fieldValue is nil, and IncludeNull is false + // Test Case 1: propertyValue is nil, and IncludeNull is false { key: "Skip nil value with IncludeNull false", - fieldValue: nil, + propertyValue: nil, includeNull: false, includeEmptyString: false, expectedShouldSkip: true, // Should skip because IncludeNull is false }, - // Test Case 2: fieldValue is an empty string, and IncludeEmptyString is false + // Test Case 2: propertyValue is an empty string, and IncludeEmptyString is false { key: "Skip Empty string with IncludeEmptyString false", - fieldValue: "", + propertyValue: "", includeNull: false, includeEmptyString: false, expectedShouldSkip: true, // Should skip because IncludeEmptyString is false }, - // Test Case 3: fieldValue is nil, and IncludeNull is true + // Test Case 3: propertyValue is nil, and IncludeNull is true { key: "Don't skip Nil value with IncludeNull true", - fieldValue: nil, + propertyValue: nil, includeNull: true, includeEmptyString: false, expectedShouldSkip: false, // Should not skip because IncludeNull is true }, - // Test Case 4: fieldValue is a non-empty string, and IncludeEmptyString is false + // Test Case 4: propertyValue is a non-empty string, and IncludeEmptyString is false { key: "Don't skip Non-empty string with IncludeEmptyString false", - fieldValue: "non-empty", + propertyValue: "non-empty", includeNull: false, includeEmptyString: false, expectedShouldSkip: false, // Should not skip because the string is non-empty }, - // Test Case 5: fieldValue is a non-empty string, and IncludeEmptyString is true + // Test Case 5: propertyValue is a non-empty string, and IncludeEmptyString is true { key: "Non-empty string with IncludeEmptyString true", - fieldValue: "non-empty", + propertyValue: "non-empty", includeNull: true, includeEmptyString: true, expectedShouldSkip: false, // Should not skip because IncludeEmptyString is true @@ -92,8 +92,8 @@ func TestShouldSkipNilOrEmptyValue(t *testing.T) { config: config, } - // Act: Call shouldSkipNilOrEmptyValue with the test case fieldValue - result := metricSet.shouldSkipNilOrEmptyValue(test.fieldValue) + // Act: Call shouldSkipNilOrEmptyValue with the test case propertyValue + result := metricSet.shouldSkipNilOrEmptyValue(test.propertyValue) // Assert: Check if the result matches the expected result assert.Equal(t, test.expectedShouldSkip, result) From 4ed5563fe6e7c637bad61f636851713b82b37137 Mon Sep 17 00:00:00 2001 From: herrBez Date: Tue, 18 Feb 2025 16:50:13 +0000 Subject: [PATCH 11/21] Refactor the wmi.include_null and wmi.include_empty_string to a more explicit wmi.include_null_properties and wmi.include_empty_string --- .../module/windows/_meta/config.reference.yml | 8 ++--- .../module/windows/wmi/_meta/docs.asciidoc | 4 +-- metricbeat/module/windows/wmi/config.go | 31 +++++++++--------- metricbeat/module/windows/wmi/config_test.go | 3 +- metricbeat/module/windows/wmi/wmi.go | 8 ++--- metricbeat/module/windows/wmi/wmi_test.go | 32 +++++++++---------- 6 files changed, 44 insertions(+), 42 deletions(-) diff --git a/metricbeat/module/windows/_meta/config.reference.yml b/metricbeat/module/windows/_meta/config.reference.yml index d7a2528a143d..7b7b95f86555 100644 --- a/metricbeat/module/windows/_meta/config.reference.yml +++ b/metricbeat/module/windows/_meta/config.reference.yml @@ -22,10 +22,10 @@ metricsets: ["wmi"] period: 60s wmi: - include_null: false # Exclude properties with null values from the output - include_queries: false # Do not include the query string in the output - include_empty_string: false # Exclude properties 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) + include_queries: false # Do not include the query string in the output + include_null_properties: false # Exclude properties with null values from the output + include_empty_string_properties: false # Exclude properties 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) # Default WMI namespace for all queries (if not specified per query) # Uncomment to override the default, which is "root\\cimv2". # namespace: "root\\cimv2" diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index eaa448cdd85c..93ecce5ea339 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -86,10 +86,10 @@ 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`*:: If set to `true` the metricset includes the properties that have null value in the output document. +*`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`*:: A boolean option that causes the metricset to include +*`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`. diff --git a/metricbeat/module/windows/wmi/config.go b/metricbeat/module/windows/wmi/config.go index d13338dff7be..312049ec9760 100644 --- a/metricbeat/module/windows/wmi/config.go +++ b/metricbeat/module/windows/wmi/config.go @@ -31,17 +31,17 @@ 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 properties with nil values in the final document - IncludeEmptyString bool `config:"wmi.include_empty_string"` // Specifies whether to include properties with empty string values in the final document - Host string `config:"wmi.host"` // Hostname of the remote WMI server - Domain string `config:"wmi.domain"` // Domain 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 + Host string `config:"wmi.host"` // Hostname of the remote WMI server + Domain string `config:"wmi.domain"` // Domain 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 } type QueryConfig struct { @@ -54,10 +54,11 @@ type QueryConfig struct { func NewDefaultConfig() Config { return Config{ - IncludeQueries: false, - IncludeNull: false, - Host: "localhost", - Namespace: WMIDefaultNamespace, + IncludeQueries: false, + IncludeNullProperties: false, + IncludeEmptyStringProperties: false, + Host: "localhost", + Namespace: WMIDefaultNamespace, } } diff --git a/metricbeat/module/windows/wmi/config_test.go b/metricbeat/module/windows/wmi/config_test.go index 666e01b7afd5..e5659d123dcf 100644 --- a/metricbeat/module/windows/wmi/config_test.go +++ b/metricbeat/module/windows/wmi/config_test.go @@ -30,7 +30,8 @@ func TestNewDefaultConfig(t *testing.T) { cfg := NewDefaultConfig() assert.False(t, cfg.IncludeQueries, "IncludeQueries should default to false") - assert.False(t, cfg.IncludeNull, "IncludeNull should default to false") + assert.False(t, cfg.IncludeNullProperties, "IncludeNullProperties should default to false") + assert.False(t, cfg.IncludeEmptyStringProperties, "IncludeEmptyStringProperties should default to false") assert.Equal(t, WMIDefaultNamespace, cfg.Namespace, "Namespace should default to WMIDefaultNamespace") assert.Empty(t, cfg.Queries, "Queries should default to an empty slice") } diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index 801aa8fa1069..38505d8ca4b6 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -90,12 +90,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // This function handles the skip conditions func (m *MetricSet) shouldSkipNilOrEmptyValue(propertyValue interface{}) bool { if propertyValue == nil { - if !m.config.IncludeNull { - return true // Skip if it's nil and IncludeNull is false + if !m.config.IncludeNullProperties { + return true // Skip if it's nil and IncludeNullProperties is false } } else if stringValue, ok := propertyValue.(string); ok { - if len(stringValue) == 0 && !m.config.IncludeEmptyString { - return true // Skip if it's an empty string and IncludeEmptyString is false + if len(stringValue) == 0 && !m.config.IncludeEmptyStringProperties { + return true // Skip if it's an empty string and IncludeEmptyStringProperties is false } } return false diff --git a/metricbeat/module/windows/wmi/wmi_test.go b/metricbeat/module/windows/wmi/wmi_test.go index 05345edf0e64..5382fd45ed77 100644 --- a/metricbeat/module/windows/wmi/wmi_test.go +++ b/metricbeat/module/windows/wmi/wmi_test.go @@ -33,49 +33,49 @@ func TestShouldSkipNilOrEmptyValue(t *testing.T) { includeEmptyString bool expectedShouldSkip bool }{ - // Test Case 1: propertyValue is nil, and IncludeNull is false + // Test Case 1: propertyValue is nil, and IncludeNullProperties is false { - key: "Skip nil value with IncludeNull false", + key: "Skip nil value with IncludeNullProperties false", propertyValue: nil, includeNull: false, includeEmptyString: false, - expectedShouldSkip: true, // Should skip because IncludeNull is false + expectedShouldSkip: true, // Should skip because IncludeNullProperties is false }, - // Test Case 2: propertyValue is an empty string, and IncludeEmptyString is false + // Test Case 2: propertyValue is an empty string, and IncludeEmptyStringProperties is false { - key: "Skip Empty string with IncludeEmptyString false", + key: "Skip Empty string with IncludeEmptyStringProperties false", propertyValue: "", includeNull: false, includeEmptyString: false, - expectedShouldSkip: true, // Should skip because IncludeEmptyString is false + expectedShouldSkip: true, // Should skip because IncludeEmptyStringProperties is false }, - // Test Case 3: propertyValue is nil, and IncludeNull is true + // Test Case 3: propertyValue is nil, and IncludeNullProperties is true { - key: "Don't skip Nil value with IncludeNull true", + key: "Don't skip Nil value with IncludeNullProperties true", propertyValue: nil, includeNull: true, includeEmptyString: false, - expectedShouldSkip: false, // Should not skip because IncludeNull is true + expectedShouldSkip: false, // Should not skip because IncludeNullProperties is true }, - // Test Case 4: propertyValue is a non-empty string, and IncludeEmptyString is false + // Test Case 4: propertyValue is a non-empty string, and IncludeEmptyStringProperties is false { - key: "Don't skip Non-empty string with IncludeEmptyString false", + key: "Don't skip Non-empty string with IncludeEmptyStringProperties false", propertyValue: "non-empty", includeNull: false, includeEmptyString: false, expectedShouldSkip: false, // Should not skip because the string is non-empty }, - // Test Case 5: propertyValue is a non-empty string, and IncludeEmptyString is true + // Test Case 5: propertyValue is a non-empty string, and IncludeEmptyStringProperties is true { - key: "Non-empty string with IncludeEmptyString true", + key: "Non-empty string with IncludeEmptyStringProperties true", propertyValue: "non-empty", includeNull: true, includeEmptyString: true, - expectedShouldSkip: false, // Should not skip because IncludeEmptyString is true + expectedShouldSkip: false, // Should not skip because IncludeEmptyStringProperties is true }, } @@ -84,8 +84,8 @@ func TestShouldSkipNilOrEmptyValue(t *testing.T) { // Arrange: Create a MetricSet with the configuration based on the test case config := Config{ - IncludeNull: test.includeNull, - IncludeEmptyString: test.includeEmptyString, + IncludeNullProperties: test.includeNull, + IncludeEmptyStringProperties: test.includeEmptyString, } metricSet := &MetricSet{ From afbced188d1c2fd4c289c010cde22f390f36584d Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Tue, 18 Feb 2025 17:55:08 +0100 Subject: [PATCH 12/21] Run make update --- metricbeat/docs/modules/windows.asciidoc | 10 +++++----- metricbeat/metricbeat.reference.yml | 10 +++++----- metricbeat/modules.d/windows.yml.disabled | 2 +- x-pack/metricbeat/metricbeat.reference.yml | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/metricbeat/docs/modules/windows.asciidoc b/metricbeat/docs/modules/windows.asciidoc index 9a9552be72c0..0c4630e84599 100644 --- a/metricbeat/docs/modules/windows.asciidoc +++ b/metricbeat/docs/modules/windows.asciidoc @@ -57,16 +57,16 @@ metricbeat.modules: metricsets: ["wmi"] period: 60s 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) + include_queries: false # Do not include the query string in the output + include_null_properties: false # Exclude properties with null values from the output + include_empty_string_properties: false # Exclude properties 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) # 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 + properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties - FreePhysicalMemory - FreeSpaceInPagingFiles - FreeVirtualMemory diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 0d7ceb34057c..75555a7b49cd 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -1095,16 +1095,16 @@ metricbeat.modules: metricsets: ["wmi"] period: 60s 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) + include_queries: false # Do not include the query string in the output + include_null_properties: false # Exclude properties with null values from the output + include_empty_string_properties: false # Exclude properties 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) # 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 + properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties - FreePhysicalMemory - FreeSpaceInPagingFiles - FreeVirtualMemory diff --git a/metricbeat/modules.d/windows.yml.disabled b/metricbeat/modules.d/windows.yml.disabled index c917800c1915..bf6e48b8bf30 100644 --- a/metricbeat/modules.d/windows.yml.disabled +++ b/metricbeat/modules.d/windows.yml.disabled @@ -28,7 +28,7 @@ # # 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 diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index dfb8246bc5ea..ffc3d3b0eae2 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -1726,16 +1726,16 @@ metricbeat.modules: metricsets: ["wmi"] period: 60s 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) + include_queries: false # Do not include the query string in the output + include_null_properties: false # Exclude properties with null values from the output + include_empty_string_properties: false # Exclude properties 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) # 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 + properties: # SELECT: Fields to retrieve for this WMI class. Omit the setting to fetch all properties - FreePhysicalMemory - FreeSpaceInPagingFiles - FreeVirtualMemory From c35c1e70cc117878e89148da07653fa782f5d1c6 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Tue, 18 Feb 2025 18:03:39 +0100 Subject: [PATCH 13/21] Make the warning more accurate --- metricbeat/module/windows/wmi/wmi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index 38505d8ca4b6..e11e2b339579 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -140,7 +140,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { } if len(rows) == 0 { - m.Logger().Warnf("The query '%s' did not produce results. This can be expected, but it can also be the result of querying an invalid property. Make sure all required properties do exist or check the WMI-Activity Operational Log for more information.", query) + m.Logger().Warnf("The query '%s' did not return any results. While this can be expected in case of a too strict WHERE clause, it may also indicate an invalid query. Ensure the query is correct or check the WMI-Activity Operational Log for further details.", query) } defer wmi.CloseAllInstances(rows) From 097435065d2dee621518b66b8b265609caa5a468 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 07:39:55 +0100 Subject: [PATCH 14/21] Fix typo --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 93ecce5ea339..dc1ff37ecdee 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -1,4 +1,4 @@ -The `wmi` metricset of the Windows module collect 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. +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 From 43af2279696eae7d01cf42609d5f0bc5309240e9 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 07:40:06 +0100 Subject: [PATCH 15/21] Fix linting error --- metricbeat/module/windows/wmi/utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metricbeat/module/windows/wmi/utils.go b/metricbeat/module/windows/wmi/utils.go index cdf05202590a..f6bb3feaaf1b 100644 --- a/metricbeat/module/windows/wmi/utils.go +++ b/metricbeat/module/windows/wmi/utils.go @@ -80,7 +80,7 @@ func ConvertDatetime(v string) (interface{}, error) { utcOffsetStr := v[22:] utcOffset, err := strconv.ParseInt(utcOffsetStr, 10, 16) if err != nil { - return nil, fmt.Errorf("datetime is invalid: error parsing UTC offset: %v", err) + return nil, fmt.Errorf("datetime is invalid: error parsing UTC offset: %w", err) } offsetHours := utcOffset / 60 offsetMinutes := utcOffset % 60 @@ -92,7 +92,7 @@ func ConvertDatetime(v string) (interface{}, error) { // Parse the combined datetime string using the defined layout date, err := time.Parse(WMI_DATETIME_LAYOUT+TIMEZONE_LAYOUT, dateString) if err != nil { - return nil, fmt.Errorf("datetime is invalid: error parsing the final datetime: %v", err) + return nil, fmt.Errorf("datetime is invalid: error parsing the final datetime: %w", err) } return date, err From e05a887f359a9a01ac1e28cc20713f6d71db6fb4 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 07:55:03 +0100 Subject: [PATCH 16/21] fix the example of the documentation + reorder the parameter definition to be the same of the example --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index dc1ff37ecdee..4f3eff3c8f15 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -48,8 +48,8 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma namespace: "root\\cimv2" # Default Namespace warning_threshold: 60s include_queries: true - include_null: false - include_empty_strings: false + include_null_properties: false + include_empty_strings_properties: false queries: - class: Win32_OperatingSystem properties: @@ -64,10 +64,6 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma 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.host`*:: The host for the remote WMI Connection. The default is `localhost`. *`wmi.domain`*:: The domain for the remote WMI Connection. The default is an empty string (`""`). @@ -76,14 +72,16 @@ The default is `root\cimv2`. *`wmi.password`*:: The password for the remote WMI Connection. The default is an empty string (`""`). +*`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 <> for more details. -*`wmi.queries`*:: The list of queries to execute. The list cannot be empty. See <> for the format of the queries. - *`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. @@ -92,6 +90,7 @@ properties that have a `null` value in the output document. The default value is *`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 <> for the format of the queries. [float] [[query-configuration]] From 8249b479522db6cd88b29054ccf298478c08491e Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 13:53:35 +0100 Subject: [PATCH 17/21] remove reference to fields that should have been removed with the refactoring --- metricbeat/docs/modules/windows.asciidoc | 2 +- metricbeat/metricbeat.reference.yml | 2 +- metricbeat/module/windows/_meta/config.reference.yml | 2 +- x-pack/metricbeat/metricbeat.reference.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/metricbeat/docs/modules/windows.asciidoc b/metricbeat/docs/modules/windows.asciidoc index 0c4630e84599..23f2d0487026 100644 --- a/metricbeat/docs/modules/windows.asciidoc +++ b/metricbeat/docs/modules/windows.asciidoc @@ -66,7 +66,7 @@ metricbeat.modules: # namespace: "root\\cimv2" queries: - class: Win32_OperatingSystem # FROM: Class to fetch - properties: # 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 diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 75555a7b49cd..c6bce3f1fe0d 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -1104,7 +1104,7 @@ metricbeat.modules: # namespace: "root\\cimv2" queries: - class: Win32_OperatingSystem # FROM: Class to fetch - properties: # 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 diff --git a/metricbeat/module/windows/_meta/config.reference.yml b/metricbeat/module/windows/_meta/config.reference.yml index 7b7b95f86555..219f7b59663e 100644 --- a/metricbeat/module/windows/_meta/config.reference.yml +++ b/metricbeat/module/windows/_meta/config.reference.yml @@ -31,7 +31,7 @@ # namespace: "root\\cimv2" queries: - class: Win32_OperatingSystem # FROM: Class to fetch - properties: # 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 diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index ffc3d3b0eae2..03ee212b0cff 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -1735,7 +1735,7 @@ metricbeat.modules: # namespace: "root\\cimv2" queries: - class: Win32_OperatingSystem # FROM: Class to fetch - properties: # 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 From ac2f9a9212301b01d8c93111d7c70aa1fcb2fb7b Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 16:53:53 +0100 Subject: [PATCH 18/21] fix typos and make sure the period is 10 minutes in our examples --- metricbeat/docs/modules/windows.asciidoc | 20 +++++++++++-------- metricbeat/metricbeat.reference.yml | 20 +++++++++++-------- .../module/windows/_meta/config.reference.yml | 20 +++++++++++-------- metricbeat/module/windows/_meta/config.yml | 4 ++-- .../module/windows/wmi/_meta/docs.asciidoc | 9 ++++----- metricbeat/modules.d/windows.yml.disabled | 4 ++-- x-pack/metricbeat/metricbeat.reference.yml | 20 +++++++++++-------- 7 files changed, 56 insertions(+), 41 deletions(-) diff --git a/metricbeat/docs/modules/windows.asciidoc b/metricbeat/docs/modules/windows.asciidoc index 23f2d0487026..0d92481e9509 100644 --- a/metricbeat/docs/modules/windows.asciidoc +++ b/metricbeat/docs/modules/windows.asciidoc @@ -55,24 +55,28 @@ metricbeat.modules: - module: windows metricsets: ["wmi"] - period: 60s + period: 10m wmi: - include_queries: false # Do not include the query string in the output - include_null_properties: false # Exclude properties with null values from the output - include_empty_string_properties: false # Exclude properties 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 - properties: # SELECT: Properties 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 diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index c6bce3f1fe0d..5f1288ef455a 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -1093,24 +1093,28 @@ metricbeat.modules: - module: windows metricsets: ["wmi"] - period: 60s + period: 10m wmi: - include_queries: false # Do not include the query string in the output - include_null_properties: false # Exclude properties with null values from the output - include_empty_string_properties: false # Exclude properties 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 - properties: # SELECT: Properties 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 diff --git a/metricbeat/module/windows/_meta/config.reference.yml b/metricbeat/module/windows/_meta/config.reference.yml index 219f7b59663e..bbbc123e0c86 100644 --- a/metricbeat/module/windows/_meta/config.reference.yml +++ b/metricbeat/module/windows/_meta/config.reference.yml @@ -20,24 +20,28 @@ - module: windows metricsets: ["wmi"] - period: 60s + period: 10m wmi: - include_queries: false # Do not include the query string in the output - include_null_properties: false # Exclude properties with null values from the output - include_empty_string_properties: false # Exclude properties 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 - properties: # SELECT: Properties 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 diff --git a/metricbeat/module/windows/_meta/config.yml b/metricbeat/module/windows/_meta/config.yml index 3a0770946907..3b4352db4c36 100644 --- a/metricbeat/module/windows/_meta/config.yml +++ b/metricbeat/module/windows/_meta/config.yml @@ -19,9 +19,9 @@ # - 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 diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 4f3eff3c8f15..7cc2c6d2689f 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -39,14 +39,14 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma ---- - module: windows metricsets: ["wmi"] - period: 60s + period: 10m wmi: # host: localhost # domain: "" # username: test # password: foo namespace: "root\\cimv2" # Default Namespace - warning_threshold: 60s + warning_threshold: 10m include_queries: true include_null_properties: false include_empty_strings_properties: false @@ -56,7 +56,6 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma - FreePhysicalMemory - FreeSpaceInPagingFiles - NumberOfUsers - # Where Clasue where: "" - class: Win32_PowerPlan properties: [] @@ -134,7 +133,7 @@ Equivalent YAML Configuration: [float] === Best Practices -- Test your queries in isolation using the `Get-CimInstance` powerhsell cmdlet or the WMI Explorer. +- 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. @@ -150,7 +149,7 @@ Equivalent YAML Configuration: 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. diff --git a/metricbeat/modules.d/windows.yml.disabled b/metricbeat/modules.d/windows.yml.disabled index bf6e48b8bf30..172ade104749 100644 --- a/metricbeat/modules.d/windows.yml.disabled +++ b/metricbeat/modules.d/windows.yml.disabled @@ -22,9 +22,9 @@ # - 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 diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 03ee212b0cff..a391d1560419 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -1724,24 +1724,28 @@ metricbeat.modules: - module: windows metricsets: ["wmi"] - period: 60s + period: 10m wmi: - include_queries: false # Do not include the query string in the output - include_null_properties: false # Exclude properties with null values from the output - include_empty_string_properties: false # Exclude properties 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 - properties: # SELECT: Properties 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 From d2ecd8b797c67d5612747f5defa408524370e9c1 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 16:55:35 +0100 Subject: [PATCH 19/21] Fix the name of the test --- metricbeat/module/windows/wmi/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/windows/wmi/config_test.go b/metricbeat/module/windows/wmi/config_test.go index e5659d123dcf..079c835027a4 100644 --- a/metricbeat/module/windows/wmi/config_test.go +++ b/metricbeat/module/windows/wmi/config_test.go @@ -146,7 +146,7 @@ func TestQueryCompile(t *testing.T) { expectedPropertiesLength: 3, }, { - name: "Query with wildcard (*) for properties", + name: "Query with empty list for properties and Where", queryConfig: QueryConfig{ Class: "Win32_ComputerSystem", Properties: []string{}, From 3ce31bd7e1beb7aeb6f8d026e0635a3b65ba9441 Mon Sep 17 00:00:00 2001 From: Mirko Bez Date: Wed, 19 Feb 2025 17:29:16 +0100 Subject: [PATCH 20/21] Hide the Remote WMI parameters --- metricbeat/module/windows/wmi/_meta/docs.asciidoc | 12 ------------ metricbeat/module/windows/wmi/config.go | 11 +++++++---- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/docs.asciidoc b/metricbeat/module/windows/wmi/_meta/docs.asciidoc index 7cc2c6d2689f..f4ca4dc47cbe 100644 --- a/metricbeat/module/windows/wmi/_meta/docs.asciidoc +++ b/metricbeat/module/windows/wmi/_meta/docs.asciidoc @@ -41,10 +41,6 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma metricsets: ["wmi"] period: 10m wmi: - # host: localhost - # domain: "" - # username: test - # password: foo namespace: "root\\cimv2" # Default Namespace warning_threshold: 10m include_queries: true @@ -63,14 +59,6 @@ NOTE: While Metricbeat stops waiting for the result, the underlying WMI query ma namespace: "root\\cimv2\\power" # Overwrites the module namespace in this query ---- -*`wmi.host`*:: The host for the remote WMI Connection. The default is `localhost`. - -*`wmi.domain`*:: The domain for the remote WMI Connection. The default is an empty string (`""`). - -*`wmi.username`*:: The username for the remote WMI Connection. The default is an empty string (`""`). - -*`wmi.password`*:: The password for the remote WMI Connection. The default is an empty string (`""`). - *`wmi.namespace`*:: The default WMI namespace used for queries. This can be overridden per query. The default is `root\cimv2`. diff --git a/metricbeat/module/windows/wmi/config.go b/metricbeat/module/windows/wmi/config.go index 312049ec9760..d1f642a6c20c 100644 --- a/metricbeat/module/windows/wmi/config.go +++ b/metricbeat/module/windows/wmi/config.go @@ -34,14 +34,17 @@ type Config struct { 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 - Host string `config:"wmi.host"` // Hostname of the remote WMI server - Domain string `config:"wmi.domain"` // Domain 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 + // 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 { From 28ed9a740cd5e0fa3b5ef121b94b2e076133a497 Mon Sep 17 00:00:00 2001 From: herrBez Date: Tue, 4 Mar 2025 07:53:19 +0000 Subject: [PATCH 21/21] Remove remote wmi logic from wmi.go. This can be reactivated by need --- metricbeat/module/windows/wmi/_meta/data.json | 1 - metricbeat/module/windows/wmi/wmi.go | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/metricbeat/module/windows/wmi/_meta/data.json b/metricbeat/module/windows/wmi/_meta/data.json index f8f6205791d3..7bfda4bff677 100644 --- a/metricbeat/module/windows/wmi/_meta/data.json +++ b/metricbeat/module/windows/wmi/_meta/data.json @@ -20,7 +20,6 @@ "LocalDateTime": "2024-12-12T15:46:39.62Z", "NumberOfUsers": 1, "class": "Win32_OperatingSystem", - "host": "localhost", "namespace": "root\\cimv2" } } diff --git a/metricbeat/module/windows/wmi/wmi.go b/metricbeat/module/windows/wmi/wmi.go index e11e2b339579..6bf56568d167 100644 --- a/metricbeat/module/windows/wmi/wmi.go +++ b/metricbeat/module/windows/wmi/wmi.go @@ -150,13 +150,15 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { MetricSetFields: mapstr.M{ "class": queryConfig.Class, "namespace": namespace, - "host": m.config.Host, + // Remote WMI is intentionally hidden, this will always be localhost + // "host": m.config.Host, }, } - if m.config.Domain != "" { - event.MetricSetFields.Put("domain", m.config.Domain) - } + // Remote WMI is intentionally hidden, this will always be the empty string + // if m.config.Domain != "" { + // event.MetricSetFields.Put("domain", m.config.Domain) + // } if m.config.IncludeQueries { event.MetricSetFields.Put("query", query)