Skip to content

Commit 0f3d0b3

Browse files
[receiver/azuremonitor] Add support for azure workload identity auth (#24543)
Enhances azuremonitor receiver authentication by using [azure workload identity](https://azure.github.io/azure-workload-identity/docs/). I have added a new config parameter `auth` to specify the used authentication method (default set to service-principal) in order to possibly add more methods in the future. Fixes #24451 **Testing:** Tested on my AKS
1 parent 0158c5f commit 0f3d0b3

File tree

9 files changed

+198
-66
lines changed

9 files changed

+198
-66
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Use this changelog template to create an entry for release notes.
2+
# If your change doesn't affect end users, such as a test fix or a tooling change,
3+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
4+
5+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
6+
change_type: enhancement
7+
8+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
9+
component: receiver/azuremonitorreceiver
10+
11+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
12+
note: Add support for authenticating using AD workload identity
13+
14+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
15+
issues: [24451]
16+
17+
# (Optional) One or more lines of additional information to render under the primary note.
18+
# These lines will be padded with 2 spaces and then inserted directly into the document.
19+
# Use pipe (|) for multiline entries.
20+
subtext:

receiver/azuremonitorreceiver/README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,29 @@ This receiver scrapes Azure Monitor API for resources metrics.
1818

1919
The following settings are required:
2020
- `subscription_id`
21-
- `tenant_id`
22-
- `client_id`
23-
- `client_secret`
2421

2522
The following settings are optional:
23+
- `auth` (default = service_principal): Specifies the used authentication method. Supported values are `service_principal`, `workload_identity`.
2624
- `resource_groups` (default = none): Filter metrics for specific resource groups, not setting a value will scrape metrics for all resources in the subscription.
2725
- `services` (default = none): Filter metrics for specific services, not setting a value will scrape metrics for all services integrated with Azure Monitor.
2826
- `cache_resources` (default = 86400): List of resources will be cached for the provided amount of time in seconds.
2927
- `cache_resources_definitions` (default = 86400): List of metrics definitions will be cached for the provided amount of time in seconds.
3028
- `maximum_number_of_metrics_in_a_call` (default = 20): Maximum number of metrics to fetch in per API call, current limit in Azure is 20 (as of 03/27/2023).
3129
- `initial_delay` (default = `1s`): defines how long this receiver waits before starting.
3230

33-
### Example Configuration
31+
Authenticating using service principal requires following additional settings:
32+
- `tenant_id`
33+
- `client_id`
34+
- `client_secret`
35+
36+
Authenticating using workload identities requires following additional settings:
37+
- `tenant_id`
38+
- `client_id`
39+
- `federate_token_file`
40+
41+
### Example Configurations
3442

43+
Using Service Principal for authentication:
3544
```yaml
3645
receivers:
3746
azuremonitor:
@@ -49,6 +58,17 @@ receivers:
4958
initial_delay: 1s
5059
```
5160
61+
Using Azure Workload Identity for authentication:
62+
```yaml
63+
receivers:
64+
azuremonitor:
65+
subscription_id: "${subscription_id}"
66+
auth: "workload_identity"
67+
tenant_id: "${env:AZURE_TENANT_ID}"
68+
client_id: "${env:AZURE_CLIENT_ID}"
69+
federated_token_file: "${env:AZURE_FEDERATED_TOKEN_FILE}"
70+
```
71+
5272
## Metrics
5373
5474
Details about the metrics scraped by this receiver can be found in [Supported metrics with Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/metrics-supported). This receiver adds the prefix "azure_" to all scraped metrics.

receiver/azuremonitorreceiver/config.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package azuremonitorreceiver // import "github.com/open-telemetry/opentelemetry-
55

66
import (
77
"errors"
8+
"fmt"
89

910
"go.opentelemetry.io/collector/receiver/scraperhelper"
1011
"go.uber.org/multierr"
@@ -18,6 +19,7 @@ var (
1819
errMissingSubscriptionID = errors.New(`SubscriptionID" is not specified in config`)
1920
errMissingClientID = errors.New(`ClientID" is not specified in config`)
2021
errMissingClientSecret = errors.New(`ClientSecret" is not specified in config`)
22+
errMissingFedTokenFile = errors.New(`FederatedTokenFile is not specified in config`)
2123

2224
monitorServices = []string{
2325
"Microsoft.EventGrid/eventSubscriptions",
@@ -224,9 +226,11 @@ type Config struct {
224226
scraperhelper.ScraperControllerSettings `mapstructure:",squash"`
225227
MetricsBuilderConfig metadata.MetricsBuilderConfig `mapstructure:",squash"`
226228
SubscriptionID string `mapstructure:"subscription_id"`
229+
Authentication string `mapstructure:"auth"`
227230
TenantID string `mapstructure:"tenant_id"`
228231
ClientID string `mapstructure:"client_id"`
229232
ClientSecret string `mapstructure:"client_secret"`
233+
FederatedTokenFile string `mapstructure:"federated_token_file"`
230234
ResourceGroups []string `mapstructure:"resource_groups"`
231235
Services []string `mapstructure:"services"`
232236
CacheResources float64 `mapstructure:"cache_resources"`
@@ -235,22 +239,44 @@ type Config struct {
235239
AppendTagsAsAttributes bool `mapstructure:"append_tags_as_attributes"`
236240
}
237241

242+
const (
243+
servicePrincipal = "service_principal"
244+
workloadIdentity = "workload_identity"
245+
)
246+
238247
// Validate validates the configuration by checking for missing or invalid fields
239248
func (c Config) Validate() (err error) {
240-
if c.TenantID == "" {
241-
err = multierr.Append(err, errMissingTenantID)
242-
}
243-
244249
if c.SubscriptionID == "" {
245250
err = multierr.Append(err, errMissingSubscriptionID)
246251
}
247252

248-
if c.ClientID == "" {
249-
err = multierr.Append(err, errMissingClientID)
250-
}
253+
switch c.Authentication {
254+
case servicePrincipal:
255+
if c.TenantID == "" {
256+
err = multierr.Append(err, errMissingTenantID)
257+
}
258+
259+
if c.ClientID == "" {
260+
err = multierr.Append(err, errMissingClientID)
261+
}
262+
263+
if c.ClientSecret == "" {
264+
err = multierr.Append(err, errMissingClientSecret)
265+
}
266+
case workloadIdentity:
267+
if c.TenantID == "" {
268+
err = multierr.Append(err, errMissingTenantID)
269+
}
270+
271+
if c.ClientID == "" {
272+
err = multierr.Append(err, errMissingClientID)
273+
}
251274

252-
if c.ClientSecret == "" {
253-
err = multierr.Append(err, errMissingClientSecret)
275+
if c.FederatedTokenFile == "" {
276+
err = multierr.Append(err, errMissingFedTokenFile)
277+
}
278+
default:
279+
return fmt.Errorf("authentication %v is not supported. supported authentications include [%v,%v]", c.Authentication, servicePrincipal, workloadIdentity)
254280
}
255281

256282
return

receiver/azuremonitorreceiver/factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func createDefaultConfig() component.Config {
4141
CacheResourcesDefinitions: 24 * 60 * 60,
4242
MaximumNumberOfMetricsInACall: 20,
4343
Services: monitorServices,
44+
Authentication: servicePrincipal,
4445
}
4546
}
4647

receiver/azuremonitorreceiver/factory_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func TestNewFactory(t *testing.T) {
4444
CacheResources: 24 * 60 * 60,
4545
CacheResourcesDefinitions: 24 * 60 * 60,
4646
MaximumNumberOfMetricsInACall: 20,
47+
Authentication: servicePrincipal,
4748
}
4849

4950
require.Equal(t, expectedCfg, factory.CreateDefaultConfig())

receiver/azuremonitorreceiver/go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azurem
33
go 1.20
44

55
require (
6-
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1
7-
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1
6+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0
7+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
88
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.8.0
99
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0
1010
github.com/google/go-cmp v0.5.9
@@ -21,14 +21,14 @@ require (
2121
)
2222

2323
require (
24-
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
25-
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 // indirect
24+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
25+
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
2626
github.com/cespare/xxhash/v2 v2.2.0 // indirect
2727
github.com/davecgh/go-spew v1.1.1 // indirect
2828
github.com/go-logr/logr v1.2.4 // indirect
2929
github.com/go-logr/stdr v1.2.2 // indirect
3030
github.com/gogo/protobuf v1.3.2 // indirect
31-
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
31+
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
3232
github.com/golang/protobuf v1.5.3 // indirect
3333
github.com/google/uuid v1.3.1 // indirect
3434
github.com/json-iterator/go v1.1.12 // indirect
@@ -41,7 +41,7 @@ require (
4141
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
4242
github.com/modern-go/reflect2 v1.0.2 // indirect
4343
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.86.0 // indirect
44-
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
44+
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
4545
github.com/pmezard/go-difflib v1.0.0 // indirect
4646
go.opencensus.io v0.24.0 // indirect
4747
go.opentelemetry.io/collector v0.86.0 // indirect

receiver/azuremonitorreceiver/go.sum

Lines changed: 14 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

receiver/azuremonitorreceiver/scraper.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func newScraper(conf *Config, settings receiver.CreateSettings) *azureScraper {
8383
settings: settings.TelemetrySettings,
8484
mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings),
8585
azIDCredentialsFunc: azidentity.NewClientSecretCredential,
86+
azIDWorkloadFunc: azidentity.NewWorkloadIdentityCredential,
8687
armClientFunc: armresources.NewClient,
8788
armMonitorDefinitionsClientFunc: armmonitor.NewMetricDefinitionsClient,
8889
armMonitorMetricsClientFunc: armmonitor.NewMetricsClient,
@@ -103,6 +104,7 @@ type azureScraper struct {
103104
resourcesUpdated time.Time
104105
mb *metadata.MetricsBuilder
105106
azIDCredentialsFunc func(string, string, string, *azidentity.ClientSecretCredentialOptions) (*azidentity.ClientSecretCredential, error)
107+
azIDWorkloadFunc func(options *azidentity.WorkloadIdentityCredentialOptions) (*azidentity.WorkloadIdentityCredential, error)
106108
armClientFunc func(string, azcore.TokenCredential, *arm.ClientOptions) (*armresources.Client, error)
107109
armMonitorDefinitionsClientFunc func(azcore.TokenCredential, *arm.ClientOptions) (*armmonitor.MetricDefinitionsClient, error)
108110
armMonitorMetricsClientFunc func(azcore.TokenCredential, *arm.ClientOptions) (*armmonitor.MetricsClient, error)
@@ -139,8 +141,7 @@ func (s *azureScraper) GetMetricsValuesClient() MetricsValuesClient {
139141
}
140142

141143
func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) {
142-
s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil)
143-
if err != nil {
144+
if err = s.loadCredentials(); err != nil {
144145
return err
145146
}
146147

@@ -153,6 +154,22 @@ func (s *azureScraper) start(_ context.Context, _ component.Host) (err error) {
153154
return
154155
}
155156

157+
func (s *azureScraper) loadCredentials() (err error) {
158+
switch s.cfg.Authentication {
159+
case servicePrincipal:
160+
if s.cred, err = s.azIDCredentialsFunc(s.cfg.TenantID, s.cfg.ClientID, s.cfg.ClientSecret, nil); err != nil {
161+
return err
162+
}
163+
case workloadIdentity:
164+
if s.cred, err = s.azIDWorkloadFunc(nil); err != nil {
165+
return err
166+
}
167+
default:
168+
return fmt.Errorf("unknown authentication %v", s.cfg.Authentication)
169+
}
170+
return nil
171+
}
172+
156173
func (s *azureScraper) scrape(ctx context.Context) (pmetric.Metrics, error) {
157174

158175
s.getResources(ctx)

0 commit comments

Comments
 (0)