Skip to content

Commit f4cc009

Browse files
constanca-mcelian-garciaedmocosta
authored andcommitted
[receiver/azuremonitor] Add support for azureauthextension (open-telemetry#39658)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Add support for extension `azureauth` for authentication. <!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes open-telemetry#39048. <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests added. <!--Describe the documentation added.--> #### Documentation README updated. <!--Please delete paragraphs that you did not use before submitting.--> --------- Co-authored-by: Célian GARCIA <[email protected]> Co-authored-by: Edmo Vamerlatti Costa <[email protected]>
1 parent 82c95b5 commit f4cc009

File tree

9 files changed

+322
-56
lines changed

9 files changed

+322
-56
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: azuremonitorreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for azureauthextension as a token provider for azuremonitorreceiver.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [39048]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

receiver/azuremonitorreceiver/README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ The following settings are required:
2424

2525
The following settings are optional:
2626

27-
- `credentials` (default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`, `managed_identity`, `default_credentials`.
27+
- `auth.authenticator`: Specifies the component ID to use to authenticate requests to Azure API. Use [azureauth extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/azureauthextension).
28+
- `credentials` (Deprecated [v0.126.0]: use `auth` instead)(default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`, `managed_identity`, `default_credentials`.
2829
- `resource_groups` (default = none): Filter metrics for specific resource groups, not setting a value will scrape metrics for all resources in the subscription.
2930
- `services` (default = none): Filter metrics for specific services, not setting a value will scrape metrics for all services integrated with Azure Monitor.
3031
- `metrics` (default = none): Filter metrics by name and aggregations. Not setting a value will scrape all metrics and their aggregations.
@@ -148,6 +149,21 @@ receivers:
148149
credentials: "default_credentials"
149150
```
150151
152+
[Using `azureauthextension`](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/azureauthextension#azure-authenticator-extension) for authentication:
153+
154+
```yaml
155+
receivers:
156+
azuremonitor:
157+
subscription_ids: ["${subscription_id}"]
158+
auth:
159+
authenticator: azureauth
160+
161+
extensions:
162+
azureauth:
163+
managed_identity:
164+
client_id: ${client_id}
165+
```
166+
151167
Overriding dimensions for a particular metric:
152168

153169
```yaml

receiver/azuremonitorreceiver/config.go

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99

10+
"go.opentelemetry.io/collector/component"
1011
"go.opentelemetry.io/collector/scraper/scraperhelper"
1112
"go.uber.org/multierr"
1213

@@ -242,11 +243,7 @@ type Config struct {
242243
Cloud string `mapstructure:"cloud"`
243244
SubscriptionIDs []string `mapstructure:"subscription_ids"`
244245
DiscoverSubscriptions bool `mapstructure:"discover_subscriptions"`
245-
Credentials string `mapstructure:"credentials"`
246246
TenantID string `mapstructure:"tenant_id"`
247-
ClientID string `mapstructure:"client_id"`
248-
ClientSecret string `mapstructure:"client_secret"`
249-
FederatedTokenFile string `mapstructure:"federated_token_file"`
250247
ResourceGroups []string `mapstructure:"resource_groups"`
251248
Services []string `mapstructure:"services"`
252249
Metrics NestedListAlias `mapstructure:"metrics"`
@@ -257,6 +254,25 @@ type Config struct {
257254
AppendTagsAsAttributes bool `mapstructure:"append_tags_as_attributes"`
258255
UseBatchAPI bool `mapstructure:"use_batch_api"`
259256
Dimensions DimensionsConfig `mapstructure:"dimensions"`
257+
258+
// Authentication accepts the component azureauthextension,
259+
// and uses it to get an access token to make requests.
260+
// Using this, `credentials` and any related fields become
261+
// useless.
262+
Authentication *AuthConfig `mapstructure:"auth"`
263+
264+
// Credentials is deprecated.
265+
Credentials string `mapstructure:"credentials"`
266+
ClientID string `mapstructure:"client_id"`
267+
ClientSecret string `mapstructure:"client_secret"`
268+
FederatedTokenFile string `mapstructure:"federated_token_file"`
269+
}
270+
271+
// AuthConfig defines the auth settings for the azure receiver.
272+
type AuthConfig struct {
273+
// AuthenticatorID specifies the name of the azure extension to authenticate the
274+
// requests to azure monitor.
275+
AuthenticatorID component.ID `mapstructure:"authenticator,omitempty"`
260276
}
261277

262278
const (
@@ -272,36 +288,39 @@ func (c Config) Validate() (err error) {
272288
err = multierr.Append(err, errMissingSubscriptionIDs)
273289
}
274290

275-
switch c.Credentials {
276-
case servicePrincipal:
277-
if c.TenantID == "" {
278-
err = multierr.Append(err, errMissingTenantID)
279-
}
291+
if c.Authentication == nil {
292+
// only matters if there is no auth specified
293+
switch c.Credentials {
294+
case servicePrincipal:
295+
if c.TenantID == "" {
296+
err = multierr.Append(err, errMissingTenantID)
297+
}
280298

281-
if c.ClientID == "" {
282-
err = multierr.Append(err, errMissingClientID)
283-
}
299+
if c.ClientID == "" {
300+
err = multierr.Append(err, errMissingClientID)
301+
}
284302

285-
if c.ClientSecret == "" {
286-
err = multierr.Append(err, errMissingClientSecret)
287-
}
288-
case workloadIdentity:
289-
if c.TenantID == "" {
290-
err = multierr.Append(err, errMissingTenantID)
291-
}
303+
if c.ClientSecret == "" {
304+
err = multierr.Append(err, errMissingClientSecret)
305+
}
306+
case workloadIdentity:
307+
if c.TenantID == "" {
308+
err = multierr.Append(err, errMissingTenantID)
309+
}
292310

293-
if c.ClientID == "" {
294-
err = multierr.Append(err, errMissingClientID)
295-
}
311+
if c.ClientID == "" {
312+
err = multierr.Append(err, errMissingClientID)
313+
}
296314

297-
if c.FederatedTokenFile == "" {
298-
err = multierr.Append(err, errMissingFedTokenFile)
299-
}
315+
if c.FederatedTokenFile == "" {
316+
err = multierr.Append(err, errMissingFedTokenFile)
317+
}
300318

301-
case managedIdentity:
302-
case defaultCredentials:
303-
default:
304-
return fmt.Errorf("credentials %v is not supported. supported authentications include [%v,%v,%v,%v]", c.Credentials, servicePrincipal, workloadIdentity, managedIdentity, defaultCredentials)
319+
case managedIdentity:
320+
case defaultCredentials:
321+
default:
322+
return fmt.Errorf("credentials %q is not supported. supported authentications include [%v,%v,%v,%v]", c.Credentials, servicePrincipal, workloadIdentity, managedIdentity, defaultCredentials)
323+
}
305324
}
306325

307326
if c.Cloud != azureCloud && c.Cloud != azureGovernmentCloud && c.Cloud != azureChinaCloud {
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package azuremonitorreceiver
5+
6+
import (
7+
"fmt"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
"go.opentelemetry.io/collector/component"
14+
"go.opentelemetry.io/collector/confmap/confmaptest"
15+
"go.opentelemetry.io/collector/confmap/xconfmap"
16+
17+
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azuremonitorreceiver/internal/metadata"
18+
)
19+
20+
func TestLoadConfig(t *testing.T) {
21+
t.Parallel()
22+
23+
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
24+
require.NoError(t, err)
25+
26+
tests := []struct {
27+
id component.ID
28+
expected component.Config
29+
expectedErr string
30+
}{
31+
{
32+
id: component.NewIDWithName(metadata.Type, "valid_subscription_ids"),
33+
expected: func() component.Config {
34+
cfg := createDefaultConfig().(*Config)
35+
cfg.SubscriptionIDs = []string{"test"}
36+
cfg.Credentials = defaultCredentials
37+
return cfg
38+
}(),
39+
},
40+
{
41+
id: component.NewIDWithName(metadata.Type, "valid_discover_subscription"),
42+
expected: func() component.Config {
43+
cfg := createDefaultConfig().(*Config)
44+
cfg.DiscoverSubscriptions = true
45+
cfg.Credentials = defaultCredentials
46+
return cfg
47+
}(),
48+
},
49+
{
50+
id: component.NewIDWithName(metadata.Type, "missing_subscription"),
51+
expectedErr: errMissingSubscriptionIDs.Error(),
52+
},
53+
{
54+
id: component.NewIDWithName(metadata.Type, "invalid_cloud"),
55+
expectedErr: errInvalidCloud.Error(),
56+
},
57+
{
58+
id: component.NewIDWithName(metadata.Type, "missing_service_principal"),
59+
expectedErr: fmt.Sprintf(
60+
"%s; %s; %s",
61+
errMissingTenantID.Error(),
62+
errMissingClientID.Error(),
63+
errMissingClientSecret.Error(),
64+
),
65+
},
66+
{
67+
id: component.NewIDWithName(metadata.Type, "missing_service_principal"),
68+
expectedErr: fmt.Sprintf(
69+
"%s; %s; %s",
70+
errMissingTenantID.Error(),
71+
errMissingClientID.Error(),
72+
errMissingClientSecret.Error(),
73+
),
74+
},
75+
{
76+
id: component.NewIDWithName(metadata.Type, "missing_workload_identity"),
77+
expectedErr: fmt.Sprintf(
78+
"%s; %s; %s",
79+
errMissingTenantID.Error(),
80+
errMissingClientID.Error(),
81+
errMissingFedTokenFile.Error(),
82+
),
83+
},
84+
{
85+
id: component.NewIDWithName(metadata.Type, "invalid_credentials"),
86+
expectedErr: `credentials "invalid" is not supported`,
87+
},
88+
{
89+
id: component.NewIDWithName(metadata.Type, "valid_authenticator"),
90+
expected: func() component.Config {
91+
cfg := createDefaultConfig().(*Config)
92+
cfg.DiscoverSubscriptions = true
93+
cfg.Authentication = &AuthConfig{
94+
AuthenticatorID: component.MustNewIDWithName("azureauth", "monitor"),
95+
}
96+
cfg.Credentials = "does-not-matter"
97+
return cfg
98+
}(),
99+
},
100+
{
101+
id: component.NewIDWithName(metadata.Type, "valid_authenticator_2"),
102+
expected: func() component.Config {
103+
cfg := createDefaultConfig().(*Config)
104+
cfg.DiscoverSubscriptions = true
105+
cfg.Authentication = &AuthConfig{
106+
AuthenticatorID: component.MustNewID("azureauth"),
107+
}
108+
cfg.Credentials = "does-not-matter"
109+
return cfg
110+
}(),
111+
},
112+
}
113+
114+
for _, tt := range tests {
115+
t.Run(tt.id.Name(), func(t *testing.T) {
116+
factory := NewFactory()
117+
cfg := factory.CreateDefaultConfig()
118+
119+
sub, err := cm.Sub(tt.id.String())
120+
require.NoError(t, err)
121+
require.NoError(t, sub.Unmarshal(cfg))
122+
123+
err = xconfmap.Validate(cfg)
124+
if tt.expectedErr != "" {
125+
assert.ErrorContains(t, err, tt.expectedErr)
126+
} else {
127+
assert.NoError(t, err)
128+
assert.Equal(t, tt.expected, cfg)
129+
}
130+
})
131+
}
132+
}

receiver/azuremonitorreceiver/go.mod

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.23.0
44

55
require (
66
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
7-
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
7+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0
88
github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics v1.2.0
99
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0
1010
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.0.0
@@ -28,6 +28,8 @@ require (
2828
go.uber.org/zap v1.27.0
2929
)
3030

31+
require go.opentelemetry.io/collector/confmap/xconfmap v0.126.0
32+
3133
require (
3234
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
3335
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
@@ -69,12 +71,12 @@ require (
6971
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
7072
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
7173
go.opentelemetry.io/otel/trace v1.35.0 // indirect
72-
golang.org/x/crypto v0.37.0 // indirect
73-
golang.org/x/net v0.39.0 // indirect
74-
golang.org/x/sys v0.32.0 // indirect
75-
golang.org/x/text v0.24.0 // indirect
76-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
77-
google.golang.org/grpc v1.72.0 // indirect
74+
golang.org/x/crypto v0.38.0 // indirect
75+
golang.org/x/net v0.40.0 // indirect
76+
golang.org/x/sys v0.33.0 // indirect
77+
golang.org/x/text v0.25.0 // indirect
78+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect
79+
google.golang.org/grpc v1.72.1 // indirect
7880
google.golang.org/protobuf v1.36.6 // indirect
7981
gopkg.in/yaml.v3 v3.0.1 // indirect
8082
sigs.k8s.io/yaml v1.4.0 // indirect

0 commit comments

Comments
 (0)