From eef54b6a33962ed2d7054deac83a908e88a625b6 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Mon, 11 Mar 2024 21:22:05 +0100 Subject: [PATCH] feat(inputs.win_wmi): Add support for remote queries --- plugins/inputs/win_wmi/README.md | 15 +++++++++++ plugins/inputs/win_wmi/query.go | 41 +++++++++++++++++++++++++++--- plugins/inputs/win_wmi/sample.conf | 6 +++++ plugins/inputs/win_wmi/win_wmi.go | 10 +++++--- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/plugins/inputs/win_wmi/README.md b/plugins/inputs/win_wmi/README.md index 748858731e6fd..ebf8ee9417543 100644 --- a/plugins/inputs/win_wmi/README.md +++ b/plugins/inputs/win_wmi/README.md @@ -19,12 +19,27 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. [CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins +## Secret-store support + +This plugin supports secrets from secret-stores for the `username` and +`password` option. +See the [secret-store documentation][SECRETSTORE] for more details on how +to use them. + +[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets + ## Configuration ```toml @sample.conf # Input plugin to query Windows Management Instrumentation # This plugin ONLY supports Windows [[inputs.win_wmi]] + ## Hostname or IP for remote connections, by default the local machine is queried + # host = "" + ## Credentials for the connection, by default no credentials are used + # username = "" + # password = "" + [[inputs.win_wmi.query]] # a string representing the WMI namespace to be queried namespace = "root\\cimv2" diff --git a/plugins/inputs/win_wmi/query.go b/plugins/inputs/win_wmi/query.go index d19993cb16051..43d1b408aa2b1 100644 --- a/plugins/inputs/win_wmi/query.go +++ b/plugins/inputs/win_wmi/query.go @@ -10,7 +10,9 @@ import ( "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/filter" "github.com/influxdata/telegraf/internal" ) @@ -23,11 +25,13 @@ type Query struct { Filter string `toml:"filter"` TagPropertiesInclude []string `toml:"tag_properties"` - tagFilter filter.Filter - query string + host string + query string + connectionParams []interface{} + tagFilter filter.Filter } -func (q *Query) prepare() error { +func (q *Query) prepare(host string, username, password config.Secret) error { // Compile the filter f, err := filter.Compile(q.TagPropertiesInclude) if err != nil { @@ -35,6 +39,30 @@ func (q *Query) prepare() error { } q.tagFilter = f + q.host = host + if q.host != "" { + q.connectionParams = append(q.connectionParams, q.host) + } else { + q.connectionParams = append(q.connectionParams, nil) + } + q.connectionParams = append(q.connectionParams, q.Namespace) + if !username.Empty() { + u, err := username.Get() + if err != nil { + return fmt.Errorf("getting username secret failed: %w", err) + } + q.connectionParams = append(q.connectionParams, u.String()) + username.Destroy() + } + if !password.Empty() { + p, err := password.Get() + if err != nil { + return fmt.Errorf("getting password secret failed: %w", err) + } + q.connectionParams = append(q.connectionParams, p.String()) + password.Destroy() + } + // Construct the overall query from the given parts wql := fmt.Sprintf("SELECT %s FROM %s", strings.Join(q.Properties, ", "), q.ClassName) if len(q.Filter) > 0 { @@ -78,7 +106,8 @@ func (q *Query) execute(acc telegraf.Accumulator) error { defer wmi.Release() // service is a SWbemServices - serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", nil, q.Namespace) + serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.connectionParams...) + if err != nil { return fmt.Errorf("failed calling method ConnectServer: %w", err) } @@ -116,6 +145,10 @@ func (q *Query) execute(acc telegraf.Accumulator) error { func (q *Query) extractProperties(acc telegraf.Accumulator, itemRaw *ole.VARIANT) error { tags, fields := map[string]string{}, map[string]interface{}{} + if q.host != "" { + tags["source"] = q.host + } + item := itemRaw.ToIDispatch() defer item.Release() diff --git a/plugins/inputs/win_wmi/sample.conf b/plugins/inputs/win_wmi/sample.conf index 8f88374cae561..fda02199f8938 100644 --- a/plugins/inputs/win_wmi/sample.conf +++ b/plugins/inputs/win_wmi/sample.conf @@ -1,6 +1,12 @@ # Input plugin to query Windows Management Instrumentation # This plugin ONLY supports Windows [[inputs.win_wmi]] + ## Hostname or IP for remote connections, by default the local machine is queried + # host = "" + ## Credentials for the connection, by default no credentials are used + # username = "" + # password = "" + [[inputs.win_wmi.query]] # a string representing the WMI namespace to be queried namespace = "root\\cimv2" diff --git a/plugins/inputs/win_wmi/win_wmi.go b/plugins/inputs/win_wmi/win_wmi.go index 0e584e74d0a72..cf2c2a9e52dff 100644 --- a/plugins/inputs/win_wmi/win_wmi.go +++ b/plugins/inputs/win_wmi/win_wmi.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/config" "github.com/influxdata/telegraf/plugins/inputs" ) @@ -17,8 +18,11 @@ var sampleConfig string // Wmi struct type Wmi struct { - Queries []Query `toml:"query"` - Log telegraf.Logger `toml:"-"` + Host string `toml:"host"` + Username config.Secret `toml:"username"` + Password config.Secret `toml:"password"` + Queries []Query `toml:"query"` + Log telegraf.Logger `toml:"-"` } // S_FALSE is returned by CoInitializeEx if it was already called on this thread. @@ -28,7 +32,7 @@ const sFalse = 0x00000001 func (w *Wmi) Init() error { for i := range w.Queries { q := &w.Queries[i] - if err := q.prepare(); err != nil { + if err := q.prepare(w.Host, w.Username, w.Password); err != nil { return fmt.Errorf("preparing query %q failed: %w", q.ClassName, err) } }