Skip to content

Commit 73c3663

Browse files
[Azure App Insights] add support for Client Secret (#48880)
* [Azure App Insights] add support for ouath2 * check that at least one auth is configured * add changelog fragemnt and update docs * small fixes * fix yml files * add auth_type to config * remove sensitive data from debug log * remove redundant log msg * add required roles * remove logger and ctx nil check * add deprecation message and remove active_directory_endpoint * fix doc * add applies_to to docs
1 parent eeabffa commit 73c3663

12 files changed

Lines changed: 563 additions & 15 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# REQUIRED
2+
# Kind can be one of:
3+
# - breaking-change: a change to previously-documented behavior
4+
# - deprecation: functionality that is being removed in a later release
5+
# - bug-fix: fixes a problem in a previous version
6+
# - enhancement: extends functionality but does not break or fix existing behavior
7+
# - feature: new functionality
8+
# - known-issue: problems that we are aware of in a given version
9+
# - security: impacts on the security of a product or a user’s deployment.
10+
# - upgrade: important information for someone upgrading from a prior version
11+
# - other: does not fit into any of the other categories
12+
kind: feature
13+
14+
# REQUIRED for all kinds
15+
# Change summary; a 80ish characters long description of the change.
16+
summary: Add client secret authentication support to Azure App Insights module
17+
18+
# REQUIRED for breaking-change, deprecation, known-issue
19+
# Long description; in case the summary is not enough to describe the change
20+
# this field accommodate a description without length limits.
21+
# description:
22+
23+
# REQUIRED for breaking-change, deprecation, known-issue
24+
# impact:
25+
26+
# REQUIRED for breaking-change, deprecation, known-issue
27+
# action:
28+
29+
# REQUIRED for all kinds
30+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
31+
component: metricbeat
32+
33+
# AUTOMATED
34+
# OPTIONAL to manually add other PR URLs
35+
# PR URL: A link the PR that added the changeset.
36+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
37+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
38+
# Please provide it if you are adding a fragment for a different PR.
39+
# pr: https://github.com/owner/repo/1234
40+
41+
# AUTOMATED
42+
# OPTIONAL to manually add other issue URLs
43+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
44+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
45+
# issue: https://github.com/owner/repo/1234

docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,47 @@ This metricset allows users to retrieve application insights metrics from specif
2323
### Config options to identify resources [_config_options_to_identify_resources]
2424

2525
`application_id`
26-
: (*[]string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal.
26+
: (*string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal.
27+
28+
29+
### Authentication [_authentication]
30+
31+
Two authentication methods are supported: **Client secret (Microsoft Entra ID)** and **API key**. The method is selected using the `auth_type` option.
32+
33+
`auth_type`
34+
: (*string*) The authentication method to use. Valid values: `api_key` (default), `client_secret`.
35+
36+
#### Client secret authentication
37+
38+
{applies_to}`stack: ga 8.19.13` {applies_to}`stack: ga 9.2.7` {applies_to}`stack: ga 9.3.2`
39+
40+
Set `auth_type: "client_secret"` and provide the following options:
41+
42+
`tenant_id`
43+
: (*string*) The tenant ID of the Microsoft Entra ID (Azure Active Directory) instance. More on service principal authentication can be found here [https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal).
44+
45+
`client_id`
46+
: (*string*) The client/application ID of the service principal registered in Microsoft Entra ID.
47+
48+
`client_secret`
49+
: (*string*) The client secret associated with the service principal.
50+
51+
All three of `tenant_id`, `client_id`, and `client_secret` are required when `auth_type` is `client_secret`.
52+
53+
**Required permissions:** The service principal must be assigned a role that grants read access to Application Insights data. The minimum built-in role is **Monitoring Reader**, assigned at the Application Insights resource scope. Other roles that include the required permissions are **Monitoring Contributor**, **Contributor**, and **Owner**. For more details, see [Azure built-in roles for Monitor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/monitor).
54+
55+
#### API key authentication
56+
57+
::::{warning}
58+
Microsoft is retiring API key authentication for Application Insights on **March 31, 2026**. After this date, API key authentication will no longer work. It is recommended to migrate to [client secret authentication](#_authentication) before this deadline. For more details, see [Transition to Microsoft Entra ID authentication](https://azure.microsoft.com/en-us/updates?id=transition-to-azure-ad-to-query-data-from-azure-monitor-application-insights-by-31-march-2026).
59+
::::
60+
61+
Set `auth_type: "api_key"` (or omit `auth_type`, as it defaults to `api_key`) and provide:
2762

2863
`api_key`
29-
: (*[]string*) The API key which will be generated, more on the steps here [https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID](https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID).
64+
: (*string*) The API key which will be generated, more on the steps here [https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID](https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID).
65+
66+
**Required permissions:** The API key must be created with the **Read telemetry** permission enabled in the Azure portal (under the API Access blade of the Application Insights resource).
3067

3168

3269
### App insights metric configurations [_app_insights_metric_configurations]

docs/reference/metricbeat/metricbeat-module-azure.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ metricbeat.modules:
265265
enabled: true
266266
period: 300s
267267
application_id: ''
268+
# auth_type: "api_key" (default) or "client_secret"
269+
#auth_type: "api_key"
270+
#client_id: '${AZURE_CLIENT_ID:""}'
271+
#client_secret: '${AZURE_CLIENT_SECRET:""}'
272+
#tenant_id: '${AZURE_TENANT_ID:""}'
268273
api_key: ''
269274
metrics:
270275
- id: ["requests/count", "requests/duration"]

x-pack/metricbeat/metricbeat.reference.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,11 @@ metricbeat.modules:
407407
enabled: true
408408
period: 300s
409409
application_id: ''
410+
# auth_type: "api_key" (default) or "client_secret"
411+
#auth_type: "api_key"
412+
#client_id: '${AZURE_CLIENT_ID:""}'
413+
#client_secret: '${AZURE_CLIENT_SECRET:""}'
414+
#tenant_id: '${AZURE_TENANT_ID:""}'
410415
api_key: ''
411416
metrics:
412417
- id: ["requests/count", "requests/duration"]

x-pack/metricbeat/module/azure/_meta/config.reference.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@
9999
enabled: true
100100
period: 300s
101101
application_id: ''
102+
# auth_type: "api_key" (default) or "client_secret"
103+
#auth_type: "api_key"
104+
#client_id: '${AZURE_CLIENT_ID:""}'
105+
#client_secret: '${AZURE_CLIENT_SECRET:""}'
106+
#tenant_id: '${AZURE_TENANT_ID:""}'
102107
api_key: ''
103108
metrics:
104109
- id: ["requests/count", "requests/duration"]

x-pack/metricbeat/module/azure/_meta/config.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@
108108
# enabled: true
109109
# period: 300s
110110
# application_id: ''
111-
# api_key: ''
111+
# # auth_type: "api_key" (default) or "client_secret"
112+
# auth_type: "client_secret"
113+
# client_id: '${AZURE_CLIENT_ID:""}'
114+
# client_secret: '${AZURE_CLIENT_SECRET:""}'
115+
# tenant_id: '${AZURE_TENANT_ID:""}'
116+
# #api_key: ''
112117
# metrics:
113118
# - id: ["requests/count", "requests/duration"]
114119

x-pack/metricbeat/module/azure/app_insights/_meta/docs.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,47 @@ This metricset allows users to retrieve application insights metrics from specif
1111
### Config options to identify resources [_config_options_to_identify_resources]
1212

1313
`application_id`
14-
: (*[]string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal.
14+
: (*string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal.
15+
16+
17+
### Authentication [_authentication]
18+
19+
Two authentication methods are supported: **Client secret (Microsoft Entra ID)** and **API key**. The method is selected using the `auth_type` option.
20+
21+
`auth_type`
22+
: (*string*) The authentication method to use. Valid values: `api_key` (default), `client_secret`.
23+
24+
#### Client secret authentication
25+
26+
{applies_to}`stack: ga 8.19.13` {applies_to}`stack: ga 9.2.7` {applies_to}`stack: ga 9.3.2`
27+
28+
Set `auth_type: "client_secret"` and provide the following options:
29+
30+
`tenant_id`
31+
: (*string*) The tenant ID of the Microsoft Entra ID (Azure Active Directory) instance. More on service principal authentication can be found here [https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal).
32+
33+
`client_id`
34+
: (*string*) The client/application ID of the service principal registered in Microsoft Entra ID.
35+
36+
`client_secret`
37+
: (*string*) The client secret associated with the service principal.
38+
39+
All three of `tenant_id`, `client_id`, and `client_secret` are required when `auth_type` is `client_secret`.
40+
41+
**Required permissions:** The service principal must be assigned a role that grants read access to Application Insights data. The minimum built-in role is **Monitoring Reader**, assigned at the Application Insights resource scope. Other roles that include the required permissions are **Monitoring Contributor**, **Contributor**, and **Owner**. For more details, see [Azure built-in roles for Monitor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/monitor).
42+
43+
#### API key authentication
44+
45+
::::{warning}
46+
Microsoft is retiring API key authentication for Application Insights on **March 31, 2026**. After this date, API key authentication will no longer work. It is recommended to migrate to [client secret authentication](#_authentication) before this deadline. For more details, see [Transition to Microsoft Entra ID authentication](https://azure.microsoft.com/en-us/updates?id=transition-to-azure-ad-to-query-data-from-azure-monitor-application-insights-by-31-march-2026).
47+
::::
48+
49+
Set `auth_type: "api_key"` (or omit `auth_type`, as it defaults to `api_key`) and provide:
1550

1651
`api_key`
17-
: (*[]string*) The API key which will be generated, more on the steps here [https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID](https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID).
52+
: (*string*) The API key which will be generated, more on the steps here [https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID](https://dev.applicationinsights.io/documentation/Authorization/API-key-and-App-ID).
53+
54+
**Required permissions:** The API key must be created with the **Read telemetry** permission enabled in the Azure portal (under the API Access blade of the Application Insights resource).
1855

1956

2057
### App insights metric configurations [_app_insights_metric_configurations]

x-pack/metricbeat/module/azure/app_insights/app_insights.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,69 @@ import (
1616
"github.com/elastic/elastic-agent-libs/logp"
1717
)
1818

19-
const metricsetName = "app_insights"
19+
const (
20+
metricsetName = "app_insights"
21+
22+
// AuthTypeAPIKey uses API key authentication (default for backwards compatibility).
23+
AuthTypeAPIKey string = "api_key"
24+
// AuthTypeClientSecret uses client secret credentials (Microsoft Entra ID).
25+
AuthTypeClientSecret string = "client_secret"
26+
)
2027

2128
// Config options
2229
type Config struct {
23-
ApplicationId string `config:"application_id" validate:"required"`
24-
ApiKey string `config:"api_key" validate:"required"`
30+
ApplicationId string `config:"application_id" validate:"required"`
2531
Period time.Duration `config:"period" validate:"nonzero,required"`
2632
Metrics []Metric `config:"metrics" validate:"required"`
2733
Namespace string `config:"namespace"`
34+
35+
// AuthType specifies the authentication method.
36+
// Valid values: api_key (default), client_secret.
37+
AuthType string `config:"auth_type"`
38+
39+
// API key authentication
40+
ApiKey string `config:"api_key"`
41+
42+
// Client secret authentication (Microsoft Entra ID)
43+
TenantId string `config:"tenant_id"`
44+
ClientId string `config:"client_id"`
45+
ClientSecret string `config:"client_secret"`
46+
}
47+
48+
// Validate checks that the authentication configuration is complete.
49+
func (c *Config) Validate() error {
50+
if c.AuthType == "" {
51+
c.AuthType = AuthTypeAPIKey
52+
}
53+
54+
switch c.AuthType {
55+
case AuthTypeAPIKey:
56+
return c.validateAPIKeyAuth()
57+
case AuthTypeClientSecret:
58+
return c.validateClientSecretAuth()
59+
default:
60+
return fmt.Errorf("unknown auth_type: %s (valid values: %s, %s)", c.AuthType, AuthTypeAPIKey, AuthTypeClientSecret)
61+
}
62+
}
63+
64+
func (c *Config) validateAPIKeyAuth() error {
65+
if c.ApiKey == "" {
66+
return fmt.Errorf("api_key is required when auth_type is %s", AuthTypeAPIKey)
67+
}
68+
return nil
69+
}
70+
71+
func (c *Config) validateClientSecretAuth() error {
72+
if c.TenantId == "" {
73+
return fmt.Errorf("tenant_id is required when auth_type is %s", AuthTypeClientSecret)
74+
}
75+
if c.ClientId == "" {
76+
return fmt.Errorf("client_id is required when auth_type is %s", AuthTypeClientSecret)
77+
}
78+
if c.ClientSecret == "" {
79+
return fmt.Errorf("client_secret is required when auth_type is %s", AuthTypeClientSecret)
80+
}
81+
return nil
2882
}
2983

3084
// Metric struct used for configuration options

x-pack/metricbeat/module/azure/app_insights/client_test.go

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import (
1313
"github.com/Azure/azure-sdk-for-go/services/preview/appinsights/v1/insights"
1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/mock"
16+
"github.com/stretchr/testify/require"
1617

1718
"github.com/elastic/elastic-agent-libs/logp/logptest"
1819
)
1920

2021
var (
2122
config = Config{
2223
ApplicationId: "",
23-
ApiKey: "",
24+
ApiKey: "test-api-key",
2425
Metrics: []Metric{
2526
{
2627
ID: []string{"requests/count"},
@@ -51,7 +52,109 @@ func TestClient(t *testing.T) {
5152
client.Service = m
5253
results, err := client.GetMetricValues()
5354
assert.NoError(t, err)
54-
assert.Equal(t, len(*results.Value), 2)
55+
assert.Len(t, *results.Value, 2)
5556
m.AssertExpectations(t)
5657
})
5758
}
59+
60+
func TestConfigValidate(t *testing.T) {
61+
tests := []struct {
62+
name string
63+
config Config
64+
wantErr string
65+
}{
66+
{
67+
name: "valid config with API key (explicit auth_type)",
68+
config: Config{
69+
ApplicationId: "app-id",
70+
AuthType: "api_key",
71+
ApiKey: "test-api-key",
72+
},
73+
wantErr: "",
74+
},
75+
{
76+
name: "valid config with API key (default auth_type)",
77+
config: Config{
78+
ApplicationId: "app-id",
79+
ApiKey: "test-api-key",
80+
},
81+
wantErr: "",
82+
},
83+
{
84+
name: "valid config with client secret",
85+
config: Config{
86+
ApplicationId: "app-id",
87+
AuthType: "client_secret",
88+
TenantId: "tenant-id",
89+
ClientId: "client-id",
90+
ClientSecret: "client-secret",
91+
},
92+
wantErr: "",
93+
},
94+
{
95+
name: "invalid config - api_key auth_type without api_key",
96+
config: Config{
97+
ApplicationId: "app-id",
98+
AuthType: "api_key",
99+
},
100+
wantErr: "api_key is required when auth_type is api_key",
101+
},
102+
{
103+
name: "invalid config - default auth_type without api_key",
104+
config: Config{
105+
ApplicationId: "app-id",
106+
},
107+
wantErr: "api_key is required when auth_type is api_key",
108+
},
109+
{
110+
name: "invalid config - client_secret missing tenant_id",
111+
config: Config{
112+
ApplicationId: "app-id",
113+
AuthType: "client_secret",
114+
ClientId: "client-id",
115+
ClientSecret: "client-secret",
116+
},
117+
wantErr: "tenant_id is required when auth_type is client_secret",
118+
},
119+
{
120+
name: "invalid config - client_secret missing client_id",
121+
config: Config{
122+
ApplicationId: "app-id",
123+
AuthType: "client_secret",
124+
TenantId: "tenant-id",
125+
ClientSecret: "client-secret",
126+
},
127+
wantErr: "client_id is required when auth_type is client_secret",
128+
},
129+
{
130+
name: "invalid config - client_secret missing client_secret",
131+
config: Config{
132+
ApplicationId: "app-id",
133+
AuthType: "client_secret",
134+
TenantId: "tenant-id",
135+
ClientId: "client-id",
136+
},
137+
wantErr: "client_secret is required when auth_type is client_secret",
138+
},
139+
{
140+
name: "invalid config - unknown auth_type",
141+
config: Config{
142+
ApplicationId: "app-id",
143+
AuthType: "invalid",
144+
},
145+
wantErr: "unknown auth_type: invalid",
146+
},
147+
}
148+
149+
for _, tt := range tests {
150+
t.Run(tt.name, func(t *testing.T) {
151+
err := tt.config.Validate()
152+
if tt.wantErr == "" {
153+
require.NoError(t, err)
154+
} else {
155+
require.Error(t, err)
156+
assert.Contains(t, err.Error(), tt.wantErr)
157+
}
158+
})
159+
}
160+
}

0 commit comments

Comments
 (0)