From 7df8a64bfa8914d0f10bfe965e84ec65d9b29e00 Mon Sep 17 00:00:00 2001 From: jakubgalecki0 Date: Wed, 11 Mar 2026 13:36:17 +0100 Subject: [PATCH] [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 (cherry picked from commit 73c3663e8b9e88729ad7828149c51f3208a64b26) # Conflicts: # docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md # docs/reference/metricbeat/metricbeat-module-azure.md # x-pack/metricbeat/module/azure/app_insights/_meta/docs.md --- .../1771338300-app_insights_oauth2.yaml | 45 +++ ...metricbeat-metricset-azure-app_insights.md | 141 ++++++++ .../metricbeat/metricbeat-module-azure.md | 301 ++++++++++++++++++ x-pack/metricbeat/metricbeat.reference.yml | 5 + .../module/azure/_meta/config.reference.yml | 5 + .../metricbeat/module/azure/_meta/config.yml | 7 +- .../module/azure/app_insights/_meta/docs.md | 90 ++++++ .../module/azure/app_insights/app_insights.go | 60 +++- .../module/azure/app_insights/client_test.go | 107 ++++++- .../module/azure/app_insights/service.go | 83 ++++- .../module/azure/app_insights/service_test.go | 172 ++++++++++ .../metricbeat/modules.d/azure.yml.disabled | 7 +- 12 files changed, 1012 insertions(+), 11 deletions(-) create mode 100644 changelog/fragments/1771338300-app_insights_oauth2.yaml create mode 100644 docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md create mode 100644 docs/reference/metricbeat/metricbeat-module-azure.md create mode 100644 x-pack/metricbeat/module/azure/app_insights/_meta/docs.md create mode 100644 x-pack/metricbeat/module/azure/app_insights/service_test.go diff --git a/changelog/fragments/1771338300-app_insights_oauth2.yaml b/changelog/fragments/1771338300-app_insights_oauth2.yaml new file mode 100644 index 000000000000..0f6a62a5b612 --- /dev/null +++ b/changelog/fragments/1771338300-app_insights_oauth2.yaml @@ -0,0 +1,45 @@ +# REQUIRED +# Kind can be one of: +# - breaking-change: a change to previously-documented behavior +# - deprecation: functionality that is being removed in a later release +# - bug-fix: fixes a problem in a previous version +# - enhancement: extends functionality but does not break or fix existing behavior +# - feature: new functionality +# - known-issue: problems that we are aware of in a given version +# - security: impacts on the security of a product or a user’s deployment. +# - upgrade: important information for someone upgrading from a prior version +# - other: does not fit into any of the other categories +kind: feature + +# REQUIRED for all kinds +# Change summary; a 80ish characters long description of the change. +summary: Add client secret authentication support to Azure App Insights module + +# REQUIRED for breaking-change, deprecation, known-issue +# Long description; in case the summary is not enough to describe the change +# this field accommodate a description without length limits. +# description: + +# REQUIRED for breaking-change, deprecation, known-issue +# impact: + +# REQUIRED for breaking-change, deprecation, known-issue +# action: + +# REQUIRED for all kinds +# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc. +component: metricbeat + +# AUTOMATED +# OPTIONAL to manually add other PR URLs +# PR URL: A link the PR that added the changeset. +# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added. +# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number. +# Please provide it if you are adding a fragment for a different PR. +# pr: https://github.com/owner/repo/1234 + +# AUTOMATED +# OPTIONAL to manually add other issue URLs +# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of). +# If not present is automatically filled by the tooling with the issue linked to the PR number. +# issue: https://github.com/owner/repo/1234 diff --git a/docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md b/docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md new file mode 100644 index 000000000000..b82c81a0b032 --- /dev/null +++ b/docs/reference/metricbeat/metricbeat-metricset-azure-app_insights.md @@ -0,0 +1,141 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-metricset-azure-app_insights.html +applies_to: + stack: beta + serverless: beta +--- + +% This file is generated! See metricbeat/scripts/mage/docs_collector.go + +# Azure app_insights metricset [metricbeat-metricset-azure-app_insights] + +::::{warning} +This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features. +:::: + + +This is the app_insights metricset. + +This metricset allows users to retrieve application insights metrics from specified applications. + + +### Config options to identify resources [_config_options_to_identify_resources] + +`application_id` +: (*string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal. + + +### Authentication [_authentication] + +Two authentication methods are supported: **Client secret (Microsoft Entra ID)** and **API key**. The method is selected using the `auth_type` option. + +`auth_type` +: (*string*) The authentication method to use. Valid values: `api_key` (default), `client_secret`. + +#### Client secret authentication + +{applies_to}`stack: ga 8.19.13` {applies_to}`stack: ga 9.2.7` {applies_to}`stack: ga 9.3.2` + +Set `auth_type: "client_secret"` and provide the following options: + +`tenant_id` +: (*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). + +`client_id` +: (*string*) The client/application ID of the service principal registered in Microsoft Entra ID. + +`client_secret` +: (*string*) The client secret associated with the service principal. + +All three of `tenant_id`, `client_id`, and `client_secret` are required when `auth_type` is `client_secret`. + +**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). + +#### API key authentication + +::::{warning} +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). +:::: + +Set `auth_type: "api_key"` (or omit `auth_type`, as it defaults to `api_key`) and provide: + +`api_key` +: (*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). + +**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). + + +### App insights metric configurations [_app_insights_metric_configurations] + +`metrics` +: List of different metrics to collect information + +`id` +: (*[]string*) IDs of the metrics that’s being reported. Usually, the id is descriptive enough to help identify what’s measured. A list of metric names can be entered as well. Default metricsets include: `requests/count` `requests/duration` `requests/failed` `users/count``users/authenticated` `pageViews/count` `pageViews/duration` `customEvents/count` `browserTimings/processingDuration` `browserTimings/receiveDuration` `browserTimings/networkDuration` `browserTimings/sendDuration` `browserTimings/totalDuration` `dependencies/count` `dependencies/duration` `dependencies/failed` `exceptions/count` `exceptions/browser` `exceptions/server` `sessions/count` `performanceCounters/requestExecutionTime` `performanceCounters/requestsPerSecond` `performanceCounters/requestsInQueue` `performanceCounters/memoryAvailableBytes` `performanceCounters/exceptionsPerSecond` `performanceCounters/processCpuPercentage` `performanceCounters/processIOBytesPerSecond` `performanceCounters/processPrivateBytes` `performanceCounters/processorCpuPercentage` `availabilityResults/count` `availabilityResults/availabilityPercentage` `availabilityResults/duration` + +`interval` +: (*string*) The time interval to use when retrieving metric values. This is an ISO8601 duration. If interval is omitted, the metric value is aggregated across the entire timespan. If interval is supplied, the result may adjust the interval to a more appropriate size based on the timespan used for the query. + +`aggregation` +: (*[]string*) The aggregation to use when computing the metric values. To retrieve more than one aggregation at a time, separate them with a comma. If no aggregation is specified, then the default aggregation for the metric is used. + +`segment` +: (*[]string*) The name of the dimension to segment the metric values by. This dimension must be applicable to the metric you are retrieving. In this case, the metric data will be segmented in the order the dimensions are listed in the parameter. + +`top` +: (*int*) The number of segments to return. This value is only valid when segment is specified. + +`order_by` +: (*string*) The aggregation function and direction to sort the segments by. This value is only valid when segment is specified. + +`filter` +: (*string*) An expression used to filter the results. This value should be a valid OData filter expression where the keys of each clause should be applicable dimensions for the metric you are retrieving. + +Example configuration: + +```yaml +metrics: + - id: ["requests/count", "requests/failed"] + segment: "request/name" + aggregation: ["sum"] +``` + +## Fields [_fields] + +For a description of each field in the metricset, see the [exported fields](/reference/metricbeat/exported-fields-azure.md) section. + +Here is an example document generated by this metricset: + +```json +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "azure": { + "app_insights": { + "end_date": "2020-10-02T13:17:45.691Z", + "start_date": "2020-10-02T13:12:45.691Z" + }, + "application_id": "42cb59a9-d5be-400b-a5c4-69b0a00434fdf4", + "metrics": { + "requests_count": { + "sum": 0 + } + } + }, + "cloud": { + "provider": "azure" + }, + "event": { + "dataset": "azure.app_insights", + "duration": 115000, + "module": "azure" + }, + "metricset": { + "name": "app_insights", + "period": 10000 + }, + "service": { + "type": "azure" + } +} +``` diff --git a/docs/reference/metricbeat/metricbeat-module-azure.md b/docs/reference/metricbeat/metricbeat-module-azure.md new file mode 100644 index 000000000000..21f825c071a9 --- /dev/null +++ b/docs/reference/metricbeat/metricbeat-module-azure.md @@ -0,0 +1,301 @@ +--- +mapped_pages: + - https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-module-azure.html +applies_to: + stack: ga + serverless: ga +--- + +% This file is generated! See metricbeat/scripts/mage/docs_collector.go + +# Azure module [metricbeat-module-azure] + +:::::{admonition} Prefer to use {{agent}} for this use case? +Refer to the [Elastic Integrations documentation](integration-docs://reference/azure_metrics/index.md). + +::::{dropdown} Learn more +{{agent}} is a single, unified way to add monitoring for logs, metrics, and other types of data to a host. It can also protect hosts from security threats, query data from operating systems, forward data from remote services or hardware, and more. Refer to the documentation for a detailed [comparison of {{beats}} and {{agent}}](docs-content://reference/fleet/index.md). + +:::: + + +::::: + + +The Azure Monitor feature collects and aggregates logs and metrics from a variety of sources into a common data platform where it can be used for analysis, visualization, and alerting. + +The azure monitor metrics are numerical values that describe some aspect of a system at a particular point in time. They are collected at regular intervals and are identified with a timestamp, a name, a value, and one or more defining labels. + +The azure module will periodically retrieve the azure monitor metrics using the Azure REST APIs as MetricList. Additional azure API calls will be executed in order to retrieve information regarding the resources targeted by the user. + +::::{important} +Extra Azure charges on metric queries may be generated by this module. Please see [additional notes about metrics and costs](#azure-api-cost) for more details. +:::: + + + +### Dashboards [_dashboards] + +The azure module comes with several predefined dashboards for virtual machines, VM guest metrics and virtual machine scale sets. + +The VM overview dashboard shows information about CPU, memory, disk usage as well as operations per second. The two available filters help narrowing down the dashbord to specific regions and/or resource groups. For example: + +![metricbeat azure vm overview](images/metricbeat-azure-vm-overview.png) + +If VM guest metrics are enabled then the guest metrics overview dashboard can help with monitoring ASP.NET applications and SQL Server metrics. For example: + +![metricbeat azure vm guestmetrics overview](images/metricbeat-azure-vm-guestmetrics-overview.png) + +The virtual machine scale sets dashboard is similar to the VM dashboard and shows relevant health information about running vm scale sets. For example: + +![metricbeat azure vmss overview](images/metricbeat-azure-vmss-overview.png) + +The Azure storage dashboards show all relevant metrics for the blob, file, table and queue storage services: + +![metricbeat azure storage overview](images/metricbeat-azure-storage-overview.png) + +The Azure billing dashboards show relevant usage and forecast information: + +![metricbeat azure billing overview](images/metricbeat-azure-billing-overview.png) + +The Azure app_state dashboard shows relevant application insights information: + +![metricbeat azure app state overview](images/metricbeat-azure-app-state-overview.png) + + +### Module-specific configuration notes [_module_specific_configuration_notes_2] + +All the tasks executed against the Azure Monitor REST API will use the Azure Resource Manager authentication model. Therefore, all requests must be authenticated with Azure Active Directory (Azure AD). One approach to authenticate the client application is to create an Azure AD service principal and retrieve the authentication (JWT) token. For a more detailed walk-through, have a look at using Azure PowerShell to create a service principal to access resources [https://docs.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps?view=azps-2.7.0](https://docs.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps?view=azps-2.7.0). It is also possible to create a service principal via the Azure portal [https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal). Users will have to make sure the roles assigned to the application contain at least reading permissions to the monitor data, more on the roles here [https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles). + +Required credentials for the `azure` module: + +`client_id` +: The unique identifier for the application (also known as Application Id) + +`client_secret` +: The client/application secret/key + +`subscription_id` +: The unique identifier for the azure subscription + +`tenant_id` +: The unique identifier of the Azure Active Directory instance + +The azure credentials keys can be used if configured `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID` + +`resource_manager_endpoint` +: *string* Optional, by default the azure public environment will be used, to override, users can provide a specific resource manager endpoint in order to use a different azure environment. Ex: [https://management.chinacloudapi.cn](https://management.chinacloudapi.cn) for azure ChinaCloud [https://management.microsoftazure.de](https://management.microsoftazure.de) for azure GermanCloud [https://management.azure.com](https://management.azure.com) for azure PublicCloud [https://management.usgovcloudapi.net](https://management.usgovcloudapi.net) for azure USGovernmentCloud + +`active_directory_endpoint` +: *string* Optional, by default the associated active directory endpoint to the resource manager endpoint will be used, to override, users can provide a specific active directory endpoint in order to use a different azure environment. Ex: [https://login.microsoftonline.com](https://login.microsoftonline.com) for azure ChinaCloud [https://login.microsoftonline.us](https://login.microsoftonline.us) for azure GermanCloud [https://login.chinacloudapi.cn](https://login.chinacloudapi.cn) for azure PublicCloud [https://login.microsoftonline.de](https://login.microsoftonline.de) for azure USGovernmentCloud + +`resource_manager_audience` +: *string* Optional, by default we are using the azure public environment, to override, users can provide a specific resource manager audience in order to use a different azure environment. Ex: [https://management.chinacloudapi.cn/](https://management.chinacloudapi.cn/) for azure ChinaCloud [https://management.microsoftazure.de/](https://management.microsoftazure.de/) for azure GermanCloud [https://management.azure.com/](https://management.azure.com/) for azure PublicCloud [https://management.usgovcloudapi.net/](https://management.usgovcloudapi.net/) for azure USGovernmentCloud Users can also use this in case of a Hybrid Cloud model, where one may define their own audiences. + + +## Metricsets [_metricsets_10] + + +### `monitor` [_monitor] + +This metricset allows users to retrieve metrics from specified resources. Added filters can apply here as the interval of retrieving these metrics, metric names, aggregation list, namespaces and metric dimensions. The monitor metrics will have a minimum timegrain of 5 minutes, so the `period` for `monitor` metricset should be `300s` or multiples of `300s`. + + +### `compute_vm` [_compute_vm] + +This metricset will collect metrics from the virtual machines, these metrics will have a timegrain every 5 minutes, so the `period` for `compute_vm` metricset should be `300s` or multiples of `300s`. + + +### `compute_vm_scaleset` [_compute_vm_scaleset] + +This metricset will collect metrics from the virtual machine scalesets, these metrics will have a timegrain every 5 minutes, so the `period` for `compute_vm_scaleset` metricset should be `300s` or multiples of `300s`. + + +### `storage` [_storage] + +This metricset will collect metrics from the storage accounts, these metrics will have a timegrain every 5 minutes, so the `period` for `storage` metricset should be `300s` or multiples of `300s`. + + +### `container_instance` [_container_instance] + +This metricset will collect metrics from specified container groups, these metrics will have a timegrain every 5 minutes, so the `period` for `container_instance` metricset should be `300s` or multiples of `300s`. + + +### `container_registry` [_container_registry] + +This metricset will collect metrics from the container registries, these metrics will have a timegrain every 5 minutes, so the `period` for `container_registry` metricset should be `300s` or multiples of `300s`. + + +### `container_service` [_container_service] + +This metricset will collect metrics from the container services, these metrics will have a timegrain every 5 minutes, so the `period` for `container_service` metricset should be `300s` or multiples of `300s`. + + +### `database_account` [_database_account] + +This metricset will collect relevant metrics from specified database accounts, these metrics will have a timegrain every 5 minutes, so the `period` for `database_account` metricset should be `300s` or multiples of `300s`. + + +### `billing` [_billing_2] + +This metricset will collect relevant usage data and forecast information from a specific subscription, these metrics will have a timegrain every 24 hours, so the `period` for `billing` metricset should be `24h` or multiples of `24h`. + + +### `app_insights` [_app_insights] + +This metricset will collect application insights metrics, the `period` (interval) for the `app-insights` metricset is set by default at `300s`. + + +### `app_state` [_app_state] + +This metricset concentrate on the most relevant application insights metrics and provides a dashboard for visualization, the `period` (interval) for the `app_state` metricset is set by default at `300s`. + + +## Additional notes about metrics and costs [azure-api-cost] + +Costs: Metric queries are charged based on the number of standard API calls. More information on pricing here [https://azure.microsoft.com/id-id/pricing/details/monitor/](https://azure.microsoft.com/id-id/pricing/details/monitor/). + +Authentication: we are handling authentication on our side (creating/renewing the authentication token), so we advise users to use dedicated credentials for metricbeat only. + + +## Example configuration [_example_configuration] + +The Azure module supports the standard configuration options that are described in [Modules](/reference/metricbeat/configuration-metricbeat.md). Here is an example configuration: + +```yaml +metricbeat.modules: +- module: azure + metricsets: + - monitor + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + resources: + - resource_query: "resourceType eq 'Microsoft.DocumentDb/databaseAccounts'" + metrics: + - name: ["DataUsage", "DocumentCount", "DocumentQuota"] + namespace: "Microsoft.DocumentDb/databaseAccounts" + +- module: azure + metricsets: + - compute_vm + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - compute_vm_scaleset + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - storage + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - container_instance + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - container_service + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - container_registry + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - database_account + enabled: true + period: 300s + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - billing + enabled: true + period: 24h + client_id: '${AZURE_CLIENT_ID:""}' + client_secret: '${AZURE_CLIENT_SECRET:""}' + tenant_id: '${AZURE_TENANT_ID:""}' + subscription_id: '${AZURE_SUBSCRIPTION_ID:""}' + +- module: azure + metricsets: + - app_insights + enabled: true + period: 300s + application_id: '' + # auth_type: "api_key" (default) or "client_secret" + #auth_type: "api_key" + #client_id: '${AZURE_CLIENT_ID:""}' + #client_secret: '${AZURE_CLIENT_SECRET:""}' + #tenant_id: '${AZURE_TENANT_ID:""}' + api_key: '' + metrics: + - id: ["requests/count", "requests/duration"] + +- module: azure + metricsets: + - app_state + enabled: true + period: 300s + application_id: '' + api_key: '' +``` + + +## Metricsets [_metricsets] + +The following metricsets are available: + +* [app_insights](/reference/metricbeat/metricbeat-metricset-azure-app_insights.md) {applies_to}`stack: beta` +* [app_state](/reference/metricbeat/metricbeat-metricset-azure-app_state.md) {applies_to}`stack: beta` +* [billing](/reference/metricbeat/metricbeat-metricset-azure-billing.md) {applies_to}`stack: beta` +* [compute_vm](/reference/metricbeat/metricbeat-metricset-azure-compute_vm.md) +* [compute_vm_scaleset](/reference/metricbeat/metricbeat-metricset-azure-compute_vm_scaleset.md) +* [container_instance](/reference/metricbeat/metricbeat-metricset-azure-container_instance.md) +* [container_registry](/reference/metricbeat/metricbeat-metricset-azure-container_registry.md) +* [container_service](/reference/metricbeat/metricbeat-metricset-azure-container_service.md) +* [database_account](/reference/metricbeat/metricbeat-metricset-azure-database_account.md) +* [monitor](/reference/metricbeat/metricbeat-metricset-azure-monitor.md) +* [storage](/reference/metricbeat/metricbeat-metricset-azure-storage.md) diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 73e062bb4368..d0074bad925c 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -384,6 +384,11 @@ metricbeat.modules: enabled: true period: 300s application_id: '' + # auth_type: "api_key" (default) or "client_secret" + #auth_type: "api_key" + #client_id: '${AZURE_CLIENT_ID:""}' + #client_secret: '${AZURE_CLIENT_SECRET:""}' + #tenant_id: '${AZURE_TENANT_ID:""}' api_key: '' metrics: - id: ["requests/count", "requests/duration"] diff --git a/x-pack/metricbeat/module/azure/_meta/config.reference.yml b/x-pack/metricbeat/module/azure/_meta/config.reference.yml index b06e466a01f5..b469a846edf3 100644 --- a/x-pack/metricbeat/module/azure/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/azure/_meta/config.reference.yml @@ -99,6 +99,11 @@ enabled: true period: 300s application_id: '' + # auth_type: "api_key" (default) or "client_secret" + #auth_type: "api_key" + #client_id: '${AZURE_CLIENT_ID:""}' + #client_secret: '${AZURE_CLIENT_SECRET:""}' + #tenant_id: '${AZURE_TENANT_ID:""}' api_key: '' metrics: - id: ["requests/count", "requests/duration"] diff --git a/x-pack/metricbeat/module/azure/_meta/config.yml b/x-pack/metricbeat/module/azure/_meta/config.yml index f7215d4f991b..434329c4b4bd 100644 --- a/x-pack/metricbeat/module/azure/_meta/config.yml +++ b/x-pack/metricbeat/module/azure/_meta/config.yml @@ -108,7 +108,12 @@ # enabled: true # period: 300s # application_id: '' -# api_key: '' +# # auth_type: "api_key" (default) or "client_secret" +# auth_type: "client_secret" +# client_id: '${AZURE_CLIENT_ID:""}' +# client_secret: '${AZURE_CLIENT_SECRET:""}' +# tenant_id: '${AZURE_TENANT_ID:""}' +# #api_key: '' # metrics: # - id: ["requests/count", "requests/duration"] diff --git a/x-pack/metricbeat/module/azure/app_insights/_meta/docs.md b/x-pack/metricbeat/module/azure/app_insights/_meta/docs.md new file mode 100644 index 000000000000..aac0d3b2b388 --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_insights/_meta/docs.md @@ -0,0 +1,90 @@ +::::{warning} +This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features. +:::: + + +This is the app_insights metricset. + +This metricset allows users to retrieve application insights metrics from specified applications. + + +### Config options to identify resources [_config_options_to_identify_resources] + +`application_id` +: (*string*) ID of the application. This is Application ID from the API Access settings blade in the Azure portal. + + +### Authentication [_authentication] + +Two authentication methods are supported: **Client secret (Microsoft Entra ID)** and **API key**. The method is selected using the `auth_type` option. + +`auth_type` +: (*string*) The authentication method to use. Valid values: `api_key` (default), `client_secret`. + +#### Client secret authentication + +{applies_to}`stack: ga 8.19.13` {applies_to}`stack: ga 9.2.7` {applies_to}`stack: ga 9.3.2` + +Set `auth_type: "client_secret"` and provide the following options: + +`tenant_id` +: (*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). + +`client_id` +: (*string*) The client/application ID of the service principal registered in Microsoft Entra ID. + +`client_secret` +: (*string*) The client secret associated with the service principal. + +All three of `tenant_id`, `client_id`, and `client_secret` are required when `auth_type` is `client_secret`. + +**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). + +#### API key authentication + +::::{warning} +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). +:::: + +Set `auth_type: "api_key"` (or omit `auth_type`, as it defaults to `api_key`) and provide: + +`api_key` +: (*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). + +**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). + + +### App insights metric configurations [_app_insights_metric_configurations] + +`metrics` +: List of different metrics to collect information + +`id` +: (*[]string*) IDs of the metrics that’s being reported. Usually, the id is descriptive enough to help identify what’s measured. A list of metric names can be entered as well. Default metricsets include: `requests/count` `requests/duration` `requests/failed` `users/count``users/authenticated` `pageViews/count` `pageViews/duration` `customEvents/count` `browserTimings/processingDuration` `browserTimings/receiveDuration` `browserTimings/networkDuration` `browserTimings/sendDuration` `browserTimings/totalDuration` `dependencies/count` `dependencies/duration` `dependencies/failed` `exceptions/count` `exceptions/browser` `exceptions/server` `sessions/count` `performanceCounters/requestExecutionTime` `performanceCounters/requestsPerSecond` `performanceCounters/requestsInQueue` `performanceCounters/memoryAvailableBytes` `performanceCounters/exceptionsPerSecond` `performanceCounters/processCpuPercentage` `performanceCounters/processIOBytesPerSecond` `performanceCounters/processPrivateBytes` `performanceCounters/processorCpuPercentage` `availabilityResults/count` `availabilityResults/availabilityPercentage` `availabilityResults/duration` + +`interval` +: (*string*) The time interval to use when retrieving metric values. This is an ISO8601 duration. If interval is omitted, the metric value is aggregated across the entire timespan. If interval is supplied, the result may adjust the interval to a more appropriate size based on the timespan used for the query. + +`aggregation` +: (*[]string*) The aggregation to use when computing the metric values. To retrieve more than one aggregation at a time, separate them with a comma. If no aggregation is specified, then the default aggregation for the metric is used. + +`segment` +: (*[]string*) The name of the dimension to segment the metric values by. This dimension must be applicable to the metric you are retrieving. In this case, the metric data will be segmented in the order the dimensions are listed in the parameter. + +`top` +: (*int*) The number of segments to return. This value is only valid when segment is specified. + +`order_by` +: (*string*) The aggregation function and direction to sort the segments by. This value is only valid when segment is specified. + +`filter` +: (*string*) An expression used to filter the results. This value should be a valid OData filter expression where the keys of each clause should be applicable dimensions for the metric you are retrieving. + +Example configuration: + +```yaml +metrics: + - id: ["requests/count", "requests/failed"] + segment: "request/name" + aggregation: ["sum"] +``` diff --git a/x-pack/metricbeat/module/azure/app_insights/app_insights.go b/x-pack/metricbeat/module/azure/app_insights/app_insights.go index de06e11a70e3..ba01c5b7efef 100644 --- a/x-pack/metricbeat/module/azure/app_insights/app_insights.go +++ b/x-pack/metricbeat/module/azure/app_insights/app_insights.go @@ -16,15 +16,69 @@ import ( "github.com/elastic/elastic-agent-libs/logp" ) -const metricsetName = "app_insights" +const ( + metricsetName = "app_insights" + + // AuthTypeAPIKey uses API key authentication (default for backwards compatibility). + AuthTypeAPIKey string = "api_key" + // AuthTypeClientSecret uses client secret credentials (Microsoft Entra ID). + AuthTypeClientSecret string = "client_secret" +) // Config options type Config struct { - ApplicationId string `config:"application_id" validate:"required"` - ApiKey string `config:"api_key" validate:"required"` + ApplicationId string `config:"application_id" validate:"required"` Period time.Duration `config:"period" validate:"nonzero,required"` Metrics []Metric `config:"metrics" validate:"required"` Namespace string `config:"namespace"` + + // AuthType specifies the authentication method. + // Valid values: api_key (default), client_secret. + AuthType string `config:"auth_type"` + + // API key authentication + ApiKey string `config:"api_key"` + + // Client secret authentication (Microsoft Entra ID) + TenantId string `config:"tenant_id"` + ClientId string `config:"client_id"` + ClientSecret string `config:"client_secret"` +} + +// Validate checks that the authentication configuration is complete. +func (c *Config) Validate() error { + if c.AuthType == "" { + c.AuthType = AuthTypeAPIKey + } + + switch c.AuthType { + case AuthTypeAPIKey: + return c.validateAPIKeyAuth() + case AuthTypeClientSecret: + return c.validateClientSecretAuth() + default: + return fmt.Errorf("unknown auth_type: %s (valid values: %s, %s)", c.AuthType, AuthTypeAPIKey, AuthTypeClientSecret) + } +} + +func (c *Config) validateAPIKeyAuth() error { + if c.ApiKey == "" { + return fmt.Errorf("api_key is required when auth_type is %s", AuthTypeAPIKey) + } + return nil +} + +func (c *Config) validateClientSecretAuth() error { + if c.TenantId == "" { + return fmt.Errorf("tenant_id is required when auth_type is %s", AuthTypeClientSecret) + } + if c.ClientId == "" { + return fmt.Errorf("client_id is required when auth_type is %s", AuthTypeClientSecret) + } + if c.ClientSecret == "" { + return fmt.Errorf("client_secret is required when auth_type is %s", AuthTypeClientSecret) + } + return nil } // Metric struct used for configuration options diff --git a/x-pack/metricbeat/module/azure/app_insights/client_test.go b/x-pack/metricbeat/module/azure/app_insights/client_test.go index bb5405ec0c15..a655e1cd8adf 100644 --- a/x-pack/metricbeat/module/azure/app_insights/client_test.go +++ b/x-pack/metricbeat/module/azure/app_insights/client_test.go @@ -13,6 +13,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/preview/appinsights/v1/insights" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "github.com/elastic/elastic-agent-libs/logp/logptest" ) @@ -20,7 +21,7 @@ import ( var ( config = Config{ ApplicationId: "", - ApiKey: "", + ApiKey: "test-api-key", Metrics: []Metric{ { ID: []string{"requests/count"}, @@ -51,7 +52,109 @@ func TestClient(t *testing.T) { client.Service = m results, err := client.GetMetricValues() assert.NoError(t, err) - assert.Equal(t, len(*results.Value), 2) + assert.Len(t, *results.Value, 2) m.AssertExpectations(t) }) } + +func TestConfigValidate(t *testing.T) { + tests := []struct { + name string + config Config + wantErr string + }{ + { + name: "valid config with API key (explicit auth_type)", + config: Config{ + ApplicationId: "app-id", + AuthType: "api_key", + ApiKey: "test-api-key", + }, + wantErr: "", + }, + { + name: "valid config with API key (default auth_type)", + config: Config{ + ApplicationId: "app-id", + ApiKey: "test-api-key", + }, + wantErr: "", + }, + { + name: "valid config with client secret", + config: Config{ + ApplicationId: "app-id", + AuthType: "client_secret", + TenantId: "tenant-id", + ClientId: "client-id", + ClientSecret: "client-secret", + }, + wantErr: "", + }, + { + name: "invalid config - api_key auth_type without api_key", + config: Config{ + ApplicationId: "app-id", + AuthType: "api_key", + }, + wantErr: "api_key is required when auth_type is api_key", + }, + { + name: "invalid config - default auth_type without api_key", + config: Config{ + ApplicationId: "app-id", + }, + wantErr: "api_key is required when auth_type is api_key", + }, + { + name: "invalid config - client_secret missing tenant_id", + config: Config{ + ApplicationId: "app-id", + AuthType: "client_secret", + ClientId: "client-id", + ClientSecret: "client-secret", + }, + wantErr: "tenant_id is required when auth_type is client_secret", + }, + { + name: "invalid config - client_secret missing client_id", + config: Config{ + ApplicationId: "app-id", + AuthType: "client_secret", + TenantId: "tenant-id", + ClientSecret: "client-secret", + }, + wantErr: "client_id is required when auth_type is client_secret", + }, + { + name: "invalid config - client_secret missing client_secret", + config: Config{ + ApplicationId: "app-id", + AuthType: "client_secret", + TenantId: "tenant-id", + ClientId: "client-id", + }, + wantErr: "client_secret is required when auth_type is client_secret", + }, + { + name: "invalid config - unknown auth_type", + config: Config{ + ApplicationId: "app-id", + AuthType: "invalid", + }, + wantErr: "unknown auth_type: invalid", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.config.Validate() + if tt.wantErr == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + } + }) + } +} diff --git a/x-pack/metricbeat/module/azure/app_insights/service.go b/x-pack/metricbeat/module/azure/app_insights/service.go index 44e35bbe1f47..b64c6222ef87 100644 --- a/x-pack/metricbeat/module/azure/app_insights/service.go +++ b/x-pack/metricbeat/module/azure/app_insights/service.go @@ -8,17 +8,26 @@ package app_insights import ( "context" + "fmt" + "net/http" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/services/preview/appinsights/v1/insights" "github.com/Azure/go-autorest/autorest" "github.com/elastic/elastic-agent-libs/logp" ) +const ( + // appInsightsScope is the scope for Azure Application Insights API used with client secret authentication. + appInsightsScope = "https://api.applicationinsights.io/.default" +) + // AppInsightsService service wrapper to the azure sdk for go type AppInsightsService struct { metricsClient *insights.MetricsClient - eventClient *insights.EventsClient context context.Context log *logp.Logger } @@ -26,9 +35,13 @@ type AppInsightsService struct { // NewService instantiates the Azure monitoring service func NewService(config Config, logger *logp.Logger) (*AppInsightsService, error) { metricsClient := insights.NewMetricsClient() - metricsClient.Authorizer = autorest.NewAPIKeyAuthorizerWithHeaders(map[string]interface{}{ - "x-api-key": config.ApiKey, - }) + + authorizer, err := getAuthorizer(config, logger) + if err != nil { + return nil, fmt.Errorf("failed to create authorizer: %w", err) + } + metricsClient.Authorizer = authorizer + service := &AppInsightsService{ metricsClient: &metricsClient, context: context.Background(), @@ -37,6 +50,68 @@ func NewService(config Config, logger *logp.Logger) (*AppInsightsService, error) return service, nil } +// getAuthorizer returns the appropriate authorizer based on the config's auth_type. +func getAuthorizer(config Config, logger *logp.Logger) (autorest.Authorizer, error) { + switch config.AuthType { + case AuthTypeClientSecret: + logger.Debug("Using client secret authentication for App Insights") + return newClientSecretAuthorizer(config) + default: + logger.Debug("Using API key authentication for App Insights") + return autorest.NewAPIKeyAuthorizerWithHeaders(map[string]interface{}{ + "x-api-key": config.ApiKey, + }), nil + } +} + +// newClientSecretAuthorizer creates an authorizer using azidentity client secret credentials. +func newClientSecretAuthorizer(config Config) (autorest.Authorizer, error) { + credential, err := azidentity.NewClientSecretCredential( + config.TenantId, + config.ClientId, + config.ClientSecret, + nil, + ) + if err != nil { + return nil, fmt.Errorf("failed to create client secret credential: %w", err) + } + + return &tokenCredentialAuthorizer{ + credential: credential, + scopes: []string{appInsightsScope}, + }, nil +} + +// tokenCredentialAuthorizer wraps an azcore.TokenCredential to implement autorest.Authorizer. +// This allows using the modern azidentity package with the legacy autorest-based SDK. +type tokenCredentialAuthorizer struct { + credential azcore.TokenCredential + scopes []string +} + +// WithAuthorization implements autorest.Authorizer interface. +func (a *tokenCredentialAuthorizer) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + // Run the previous preparer in the chain + r, err := p.Prepare(r) + if err != nil { + return r, err + } + + token, err := a.credential.GetToken(r.Context(), policy.TokenRequestOptions{ + Scopes: a.scopes, + }) + if err != nil { + return r, fmt.Errorf("failed to get token: %w", err) + } + + r.Header.Set("Authorization", "Bearer "+token.Token) + return r, nil + }) + } +} + // GetMetricValues will return specified app insights metrics func (service *AppInsightsService) GetMetricValues(applicationId string, bodyMetrics []insights.MetricsPostBodySchema) (insights.ListMetricsResultsItem, error) { return service.metricsClient.GetMultiple(service.context, applicationId, bodyMetrics) diff --git a/x-pack/metricbeat/module/azure/app_insights/service_test.go b/x-pack/metricbeat/module/azure/app_insights/service_test.go new file mode 100644 index 000000000000..1fa40d3ce7e4 --- /dev/null +++ b/x-pack/metricbeat/module/azure/app_insights/service_test.go @@ -0,0 +1,172 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +//go:build !requirefips + +package app_insights + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/go-autorest/autorest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-agent-libs/logp/logptest" +) + +// mockTokenCredential implements azcore.TokenCredential for testing. +type mockTokenCredential struct { + token string + err error +} + +func (m *mockTokenCredential) GetToken(_ context.Context, _ policy.TokenRequestOptions) (azcore.AccessToken, error) { + if m.err != nil { + return azcore.AccessToken{}, m.err + } + return azcore.AccessToken{Token: m.token}, nil +} + +func TestGetAuthorizer(t *testing.T) { + logger := logptest.NewTestingLogger(t, "") + + t.Run("returns API key authorizer when auth_type is api_key", func(t *testing.T) { + cfg := Config{ + ApplicationId: "app-id", + AuthType: AuthTypeAPIKey, + ApiKey: "my-api-key", + } + + auth, err := getAuthorizer(cfg, logger) + require.NoError(t, err) + require.NotNil(t, auth) + + _, isTokenAuth := auth.(*tokenCredentialAuthorizer) + assert.False(t, isTokenAuth, "expected API key authorizer, got tokenCredentialAuthorizer") + }) + + t.Run("returns client secret authorizer when auth_type is client_secret", func(t *testing.T) { + cfg := Config{ + ApplicationId: "app-id", + AuthType: AuthTypeClientSecret, + TenantId: "tenant-id", + ClientId: "client-id", + ClientSecret: "client-secret", + } + + auth, err := getAuthorizer(cfg, logger) + require.NoError(t, err) + require.NotNil(t, auth) + + tokenAuth, isTokenAuth := auth.(*tokenCredentialAuthorizer) + assert.True(t, isTokenAuth, "expected tokenCredentialAuthorizer") + assert.Equal(t, []string{appInsightsScope}, tokenAuth.scopes) + }) +} + +func TestNewClientSecretAuthorizer(t *testing.T) { + t.Run("returns tokenCredentialAuthorizer with correct scopes", func(t *testing.T) { + cfg := Config{ + AuthType: AuthTypeClientSecret, + TenantId: "tenant-id", + ClientId: "client-id", + ClientSecret: "client-secret", + } + + auth, err := newClientSecretAuthorizer(cfg) + require.NoError(t, err) + require.NotNil(t, auth) + + tokenAuth, ok := auth.(*tokenCredentialAuthorizer) + require.True(t, ok, "expected *tokenCredentialAuthorizer") + assert.Equal(t, []string{appInsightsScope}, tokenAuth.scopes) + assert.NotNil(t, tokenAuth.credential) + }) + +} + +func TestTokenCredentialAuthorizer_WithAuthorization(t *testing.T) { + t.Run("sets Authorization header with bearer token", func(t *testing.T) { + auth := &tokenCredentialAuthorizer{ + credential: &mockTokenCredential{token: "test-token-123"}, + scopes: []string{appInsightsScope}, + } + + req := httptest.NewRequest(http.MethodGet, "https://api.applicationinsights.io/v1/apps/test", nil) + + decorator := auth.WithAuthorization() + preparer := decorator(autorest.CreatePreparer()) + result, err := preparer.Prepare(req) + + require.NoError(t, err) + assert.Equal(t, "Bearer test-token-123", result.Header.Get("Authorization")) + }) + + t.Run("propagates error from credential GetToken", func(t *testing.T) { + tokenErr := errors.New("token acquisition failed") + auth := &tokenCredentialAuthorizer{ + credential: &mockTokenCredential{err: tokenErr}, + scopes: []string{appInsightsScope}, + } + + req := httptest.NewRequest(http.MethodGet, "https://api.applicationinsights.io/v1/apps/test", nil) + + decorator := auth.WithAuthorization() + preparer := decorator(autorest.CreatePreparer()) + _, err := preparer.Prepare(req) + + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to get token") + assert.ErrorIs(t, err, tokenErr) + }) + + t.Run("propagates error from previous preparer", func(t *testing.T) { + auth := &tokenCredentialAuthorizer{ + credential: &mockTokenCredential{token: "test-token"}, + scopes: []string{appInsightsScope}, + } + + req := httptest.NewRequest(http.MethodGet, "https://api.applicationinsights.io/v1/apps/test", nil) + + prevErr := errors.New("previous preparer failed") + failingPreparer := autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + return r, prevErr + }) + + decorator := auth.WithAuthorization() + preparer := decorator(failingPreparer) + _, err := preparer.Prepare(req) + + require.Error(t, err) + assert.ErrorIs(t, err, prevErr) + assert.Empty(t, req.Header.Get("Authorization"), "Authorization header should not be set when previous preparer fails") + }) + + t.Run("preserves existing headers on the request", func(t *testing.T) { + auth := &tokenCredentialAuthorizer{ + credential: &mockTokenCredential{token: "test-token"}, + scopes: []string{appInsightsScope}, + } + + req := httptest.NewRequest(http.MethodGet, "https://api.applicationinsights.io/v1/apps/test", nil) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Custom-Header", "custom-value") + + decorator := auth.WithAuthorization() + preparer := decorator(autorest.CreatePreparer()) + result, err := preparer.Prepare(req) + + require.NoError(t, err) + assert.Equal(t, "Bearer test-token", result.Header.Get("Authorization")) + assert.Equal(t, "application/json", result.Header.Get("Content-Type")) + assert.Equal(t, "custom-value", result.Header.Get("X-Custom-Header")) + }) +} diff --git a/x-pack/metricbeat/modules.d/azure.yml.disabled b/x-pack/metricbeat/modules.d/azure.yml.disabled index 110356b259f8..0f0699f1056e 100644 --- a/x-pack/metricbeat/modules.d/azure.yml.disabled +++ b/x-pack/metricbeat/modules.d/azure.yml.disabled @@ -111,7 +111,12 @@ # enabled: true # period: 300s # application_id: '' -# api_key: '' +# # auth_type: "api_key" (default) or "client_secret" +# auth_type: "client_secret" +# client_id: '${AZURE_CLIENT_ID:""}' +# client_secret: '${AZURE_CLIENT_SECRET:""}' +# tenant_id: '${AZURE_TENANT_ID:""}' +# #api_key: '' # metrics: # - id: ["requests/count", "requests/duration"]