AzureMonitor: Remove support for different credentials for Azure Monitor Logs (#35121)

This commit is contained in:
Andres Martinez Gotor
2021-06-15 18:51:06 +02:00
committed by GitHub
parent abe35c8c01
commit b1a839825f
16 changed files with 555 additions and 1102 deletions
@@ -17,8 +17,5 @@ export const createMockInstanceSetttings = (): AzureDataSourceInstanceSettings =
tenantId: 'abc-123',
clientId: 'def-456',
subscriptionId: 'ghi-789',
// logs
azureLogAnalyticsSameAs: true,
},
});
@@ -13,21 +13,6 @@ export function getManagementApiRoute(azureCloud: string): string {
}
}
export function getLogAnalyticsManagementApiRoute(azureCloud: string): string {
switch (azureCloud) {
case 'azuremonitor':
return 'workspacesloganalytics';
case 'chinaazuremonitor':
return 'chinaworkspacesloganalytics';
case 'govazuremonitor':
return 'govworkspacesloganalytics';
case 'germanyazuremonitor':
return 'germanyworkspacesloganalytics';
default:
throw new Error('The cloud not supported.');
}
}
export function getLogAnalyticsApiRoute(azureCloud: string): string {
switch (azureCloud) {
case 'azuremonitor':
@@ -33,7 +33,7 @@ describe('AzureLogAnalyticsDatasource', () => {
beforeEach(() => {
ctx.instanceSettings = {
jsonData: { logAnalyticsSubscriptionId: 'xxx', azureLogAnalyticsSameAs: false },
jsonData: { subscriptionId: 'xxx' },
url: 'http://azureloganalyticsapi',
};
@@ -93,7 +93,7 @@ describe('AzureLogAnalyticsDatasource', () => {
it('should use the loganalyticsazure plugin route', async () => {
await ctx.ds.metricFindQuery('workspace("aworkspace").AzureActivity | distinct Category');
expect(workspacesUrl).toContain('workspacesloganalytics');
expect(workspacesUrl).toContain('azuremonitor');
expect(azureLogAnalyticsUrl).toContain('loganalyticsazure');
});
});
@@ -18,8 +18,8 @@ import {
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend, FetchResponse } from '@grafana/runtime';
import { Observable, from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { getAuthType, getAzureCloud, isLogAnalyticsSameAs } from '../credentials';
import { getLogAnalyticsApiRoute, getLogAnalyticsManagementApiRoute } from '../api/routes';
import { getAuthType, getAzureCloud } from '../credentials';
import { getLogAnalyticsApiRoute, getManagementApiRoute } from '../api/routes';
import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata';
import { isGUIDish } from '../components/ResourcePicker/utils';
@@ -51,17 +51,11 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
const logAnalyticsRoute = getLogAnalyticsApiRoute(cloud);
this.baseUrl = `/${logAnalyticsRoute}`;
const managementRoute = getLogAnalyticsManagementApiRoute(cloud);
const managementRoute = getManagementApiRoute(cloud);
this.azureMonitorUrl = `/${managementRoute}/subscriptions`;
this.url = instanceSettings.url || '';
const sameAsMonitor = isLogAnalyticsSameAs(instanceSettings);
this.defaultSubscriptionId = sameAsMonitor
? instanceSettings.jsonData.subscriptionId
: instanceSettings.jsonData.logAnalyticsSubscriptionId;
this.defaultSubscriptionId = this.instanceSettings.jsonData.subscriptionId || '';
this.defaultOrFirstWorkspace = this.instanceSettings.jsonData.logAnalyticsDefaultWorkspace || '';
}
@@ -485,14 +479,14 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
const authType = getAuthType(this.instanceSettings);
if (authType === 'clientsecret') {
if (!this.isValidConfigField(this.instanceSettings.jsonData.logAnalyticsTenantId)) {
if (!this.isValidConfigField(this.instanceSettings.jsonData.tenantId)) {
return {
status: 'error',
message: 'The Tenant Id field is required.',
};
}
if (!this.isValidConfigField(this.instanceSettings.jsonData.logAnalyticsClientId)) {
if (!this.isValidConfigField(this.instanceSettings.jsonData.clientId)) {
return {
status: 'error',
message: 'The Client Id field is required.',
@@ -22,18 +22,11 @@ const setup = (propsFunc?: (props: Props) => Props) => {
basicAuthPassword: '',
withCredentials: false,
isDefault: false,
secureJsonFields: {
logAnalyticsClientSecret: false,
},
secureJsonFields: {},
jsonData: {
cloudName: '',
subscriptionId: '',
azureLogAnalyticsSameAs: false,
logAnalyticsDefaultWorkspace: '',
logAnalyticsTenantId: '',
},
secureJsonData: {
logAnalyticsClientSecret: '',
},
version: 1,
readOnly: false,
@@ -80,10 +73,10 @@ describe('Render', () => {
...props.options.jsonData,
azureLogAnalyticsSameAs: false,
logAnalyticsDefaultWorkspace: '',
logAnalyticsTenantId: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
logAnalyticsClientId: '44693801-6ee6-49de-9b2d-9106972f9572',
logAnalyticsSubscriptionId: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
logAnalyticsClientSecret: 'cddcc020-2c94-460a-a3d0-df3147ffa792',
tenantId: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
clientId: '44693801-6ee6-49de-9b2d-9106972f9572',
subscriptionId: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
clientSecret: 'cddcc020-2c94-460a-a3d0-df3147ffa792',
},
},
}));
@@ -91,18 +84,8 @@ describe('Render', () => {
});
it('should not render the Switch to use different creds for log analytics by default', () => {
setup((props) => ({
...props,
options: {
...props.options,
jsonData: {
...props.options.jsonData,
azureLogAnalyticsSameAs: undefined,
},
},
}));
expect(screen.queryByLabelText('Same details as Azure Monitor API')).not.toBeInTheDocument();
expect(screen.queryByText('is deprecated', { exact: false })).not.toBeInTheDocument();
setup();
expect(screen.queryByText('is no longer supported', { exact: false })).not.toBeInTheDocument();
});
// Remove this test with deprecated code
@@ -117,7 +100,6 @@ describe('Render', () => {
},
},
}));
expect(screen.queryByLabelText('Same details as Azure Monitor API')).toBeInTheDocument();
expect(screen.queryByText('is deprecated', { exact: false })).toBeInTheDocument();
expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument();
});
});
@@ -2,15 +2,9 @@ import React, { FunctionComponent, useEffect, useMemo, useReducer, useState } fr
import { SelectableValue } from '@grafana/data';
import { AzureCredentialsForm } from './AzureCredentialsForm';
import { InlineFormLabel, LegacyForms, Button, Alert } from '@grafana/ui';
const { Select, Switch } = LegacyForms;
import { AzureDataSourceSettings, AzureCredentials } from '../types';
import {
getCredentials,
getLogAnalyticsCredentials,
isCredentialsComplete,
updateLogAnalyticsCredentials,
updateLogAnalyticsSameAs,
} from '../credentials';
const { Select } = LegacyForms;
import { AzureDataSourceSettings } from '../types';
import { getCredentials, isCredentialsComplete } from '../credentials';
export interface Props {
options: AzureDataSourceSettings;
@@ -22,22 +16,16 @@ export interface Props {
export const AnalyticsConfig: FunctionComponent<Props> = (props: Props) => {
const { updateOptions, getSubscriptions, getWorkspaces } = props;
const primaryCredentials = useMemo(() => getCredentials(props.options), [props.options]);
const logAnalyticsCredentials = useMemo(() => getLogAnalyticsCredentials(props.options), [props.options]);
const subscriptionId = logAnalyticsCredentials
? logAnalyticsCredentials.defaultSubscriptionId
: primaryCredentials.defaultSubscriptionId;
const subscriptionId = primaryCredentials.defaultSubscriptionId;
// Only show a section for setting LogAnalytics credentials if they were set from before
// And the authType is supported
const [credentialsUsed, _] = useState(!!logAnalyticsCredentials);
const credentialsEnabled = credentialsUsed && primaryCredentials.authType === 'clientsecret';
// Only show a section for setting LogAnalytics credentials if
// they were set from before with different values and the
// authType is supported
const logCredentialsEnabled =
primaryCredentials.authType === 'clientsecret' && props.options.jsonData.azureLogAnalyticsSameAs === false;
const hasRequiredFields =
subscriptionId &&
(logAnalyticsCredentials
? isCredentialsComplete(logAnalyticsCredentials)
: isCredentialsComplete(primaryCredentials));
const hasRequiredFields = subscriptionId && isCredentialsComplete(primaryCredentials);
const defaultWorkspace = props.options.jsonData.logAnalyticsDefaultWorkspace;
@@ -91,18 +79,6 @@ export const AnalyticsConfig: FunctionComponent<Props> = (props: Props) => {
}
};
const [sameAsSwitched, setSameAsSwitched] = useState(false);
const onCredentialsChange = (updatedCredentials: AzureCredentials) => {
updateOptions((options) => updateLogAnalyticsCredentials(options, updatedCredentials));
};
const onLogAnalyticsSameAsChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const sameAs = event.currentTarget.checked;
updateOptions((options) => updateLogAnalyticsSameAs(options, sameAs));
setSameAsSwitched(true);
};
const onDefaultWorkspaceChange = (selected: SelectableValue<string>) => {
updateOptions((options) => {
return {
@@ -115,55 +91,29 @@ export const AnalyticsConfig: FunctionComponent<Props> = (props: Props) => {
});
};
const tooltipAttribute = {
...(!logAnalyticsCredentials && {
tooltip: 'Workspaces are pulled from default subscription selected above.',
}),
};
const showSameAsHelpMsg =
credentialsEnabled &&
sameAsSwitched &&
primaryCredentials.authType === 'clientsecret' &&
!primaryCredentials.clientSecret;
return (
<>
<h3 className="page-heading">Azure Monitor Logs</h3>
{credentialsEnabled && (
{logCredentialsEnabled && (
<>
<Switch
label="Same details as Azure Monitor API"
checked={!logAnalyticsCredentials}
onChange={onLogAnalyticsSameAsChange}
{...tooltipAttribute}
<Alert severity="error" title="Deprecated">
Using different credentials for Azure Monitor Logs is no longer supported. Authentication information above
will be used instead. Please create a new data source with the credentials below.
</Alert>
<AzureCredentialsForm
managedIdentityEnabled={false}
credentials={{
...primaryCredentials,
authType: 'clientsecret',
// Use deprecated Log Analytics credentials read-only
// to help with a possible migration
tenantId: props.options.jsonData.logAnalyticsTenantId,
clientId: props.options.jsonData.logAnalyticsClientId,
}}
getSubscriptions={getSubscriptions}
disabled={true}
/>
{showSameAsHelpMsg && (
<div className="grafana-info-box m-t-2">
<div className="alert-body">
<p>Re-enter your Azure Monitor Client Secret to use this setting.</p>
</div>
</div>
)}
{logAnalyticsCredentials && (
<>
<Alert severity="info" title="Deprecated">
Using different credentials for Azure Monitor Logs is deprecated and will be removed in a future
version.
<br />
Create a different Data Source if you need to use different credentials.
</Alert>
<AzureCredentialsForm
managedIdentityEnabled={false}
credentials={logAnalyticsCredentials}
onCredentialsChange={onCredentialsChange}
getSubscriptions={getSubscriptions}
/>
</>
)}
</>
)}
<div className="gf-form-group">
@@ -1,6 +1,8 @@
import React from 'react';
import { shallow } from 'enzyme';
import AzureCredentialsForm, { Props } from './AzureCredentialsForm';
import { LegacyForms, Button } from '@grafana/ui';
const { Input } = LegacyForms;
const setup = (propsFunc?: (props: Props) => Props) => {
let props: Props = {
@@ -63,4 +65,26 @@ describe('Render', () => {
}));
expect(wrapper).toMatchSnapshot();
});
describe('when disabled', () => {
it('should disable inputs', () => {
const wrapper = setup((props) => ({
...props,
disabled: true,
}));
const inputs = wrapper.find(Input);
expect(inputs.length).toBeGreaterThan(1);
inputs.forEach((input) => {
expect(input.prop('disabled')).toBe(true);
});
});
it('should remove buttons', () => {
const wrapper = setup((props) => ({
...props,
disabled: true,
}));
expect(wrapper.find(Button).exists()).toBe(false);
});
});
});
@@ -9,8 +9,9 @@ export interface Props {
managedIdentityEnabled: boolean;
credentials: AzureCredentials;
azureCloudOptions?: SelectableValue[];
onCredentialsChange: (updatedCredentials: AzureCredentials) => void;
onCredentialsChange?: (updatedCredentials: AzureCredentials) => void;
getSubscriptions?: () => Promise<SelectableValue[]>;
disabled?: boolean;
}
const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
@@ -25,7 +26,7 @@ const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
];
export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) => {
const { credentials, azureCloudOptions, onCredentialsChange, getSubscriptions } = props;
const { credentials, azureCloudOptions, onCredentialsChange, getSubscriptions, disabled } = props;
const hasRequiredFields = isCredentialsComplete(credentials);
const [subscriptions, setSubscriptions] = useState<Array<SelectableValue<string>>>([]);
@@ -189,6 +190,7 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.tenantId || ''}
onChange={onTenantIdChange}
disabled={disabled}
/>
</div>
</div>
@@ -202,39 +204,42 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientId || ''}
onChange={onClientIdChange}
disabled={disabled}
/>
</div>
</div>
</div>
{typeof credentials.clientSecret === 'symbol' ? (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
<Input className="width-25" placeholder="configured" disabled={true} />
</div>
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button variant="secondary" type="button" onClick={onClientSecretReset}>
reset
</Button>
{!disabled &&
(typeof credentials.clientSecret === 'symbol' ? (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
<Input className="width-25" placeholder="configured" disabled={true} />
</div>
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button variant="secondary" type="button" onClick={onClientSecretReset} disabled={disabled}>
reset
</Button>
</div>
</div>
</div>
</div>
) : (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
<div className="width-15">
<Input
className="width-30"
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientSecret || ''}
onChange={onClientSecretChange}
/>
) : (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
<div className="width-15">
<Input
className="width-30"
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientSecret || ''}
onChange={onClientSecretChange}
disabled={disabled}
/>
</div>
</div>
</div>
</div>
)}
))}
</>
)}
{getSubscriptions && (
@@ -251,25 +256,28 @@ export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) =>
}
options={subscriptions}
onChange={onSubscriptionChange}
isDisabled={disabled}
/>
</div>
</div>
</div>
<div className="gf-form-inline">
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button
variant="secondary"
size="sm"
type="button"
onClick={onLoadSubscriptions}
disabled={!hasRequiredFields}
>
Load Subscriptions
</Button>
{!disabled && (
<div className="gf-form-inline">
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button
variant="secondary"
size="sm"
type="button"
onClick={onLoadSubscriptions}
disabled={!hasRequiredFields}
>
Load Subscriptions
</Button>
</div>
</div>
</div>
</div>
)}
</>
)}
</div>
@@ -14,7 +14,7 @@ import { InsightsConfig } from './InsightsConfig';
import ResponseParser from '../azure_monitor/response_parser';
import { AzureDataSourceJsonData, AzureDataSourceSecureJsonData, AzureDataSourceSettings } from '../types';
import { getAzureCloud, isAppInsightsConfigured } from '../credentials';
import { getLogAnalyticsManagementApiRoute, getManagementApiRoute } from '../api/routes';
import { getManagementApiRoute } from '../api/routes';
export type Props = DataSourcePluginOptionsEditorProps<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
@@ -77,7 +77,7 @@ export class ConfigEditor extends PureComponent<Props, State> {
await this.saveOptions();
const cloud = getAzureCloud(this.props.options);
const route = getLogAnalyticsManagementApiRoute(cloud);
const route = getManagementApiRoute(cloud);
const url = `/${route}/subscriptions?api-version=2019-03-01`;
const result = await getBackendSrv().datasourceRequest({
@@ -92,7 +92,7 @@ export class ConfigEditor extends PureComponent<Props, State> {
await this.saveOptions();
const cloud = getAzureCloud(this.props.options);
const route = getLogAnalyticsManagementApiRoute(cloud);
const route = getManagementApiRoute(cloud);
const url = `/${route}/subscriptions/${subscriptionId}/providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview`;
const result = await getBackendSrv().datasourceRequest({
@@ -23,6 +23,9 @@ export class InsightsConfig extends PureComponent<Props> {
return (
<>
<h3 className="page-heading">Azure Application Insights</h3>
<Alert severity="info" title="Application Insights credentials are deprecated">
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
<div className="gf-form-group">
{options.secureJsonFields.appInsightsApiKey ? (
<div className="gf-form-inline">
@@ -66,10 +69,6 @@ export class InsightsConfig extends PureComponent<Props> {
</div>
</div>
</div>
<Alert severity="info" title="Application Insights credentials are deprecated">
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
</>
);
}
@@ -7,6 +7,12 @@ exports[`Render should disable insights api key input 1`] = `
>
Azure Application Insights
</h3>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
<div
className="gf-form-group"
>
@@ -65,12 +71,6 @@ exports[`Render should disable insights api key input 1`] = `
</div>
</div>
</div>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
</Fragment>
`;
@@ -81,6 +81,12 @@ exports[`Render should enable insights api key input 1`] = `
>
Azure Application Insights
</h3>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
<div
className="gf-form-group"
>
@@ -128,12 +134,6 @@ exports[`Render should enable insights api key input 1`] = `
</div>
</div>
</div>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
</Fragment>
`;
@@ -144,6 +144,12 @@ exports[`Render should render component 1`] = `
>
Azure Application Insights
</h3>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
<div
className="gf-form-group"
>
@@ -191,11 +197,5 @@ exports[`Render should render component 1`] = `
</div>
</div>
</div>
<Alert
severity="info"
title="Application Insights credentials are deprecated"
>
Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs.
</Alert>
</Fragment>
`;
@@ -64,20 +64,6 @@ function getSecret(options: AzureDataSourceSettings): undefined | string | Conce
}
}
function getLogAnalyticsSecret(options: AzureDataSourceSettings): undefined | string | ConcealedSecret {
if (options.secureJsonFields.logAnalyticsClientSecret) {
// The secret is concealed on server
return concealed;
} else {
const secret = options.secureJsonData?.logAnalyticsClientSecret;
return typeof secret === 'string' && secret.length > 0 ? secret : undefined;
}
}
export function isLogAnalyticsSameAs(options: AzureDataSourceSettings | AzureDataSourceInstanceSettings): boolean {
return typeof options.jsonData.azureLogAnalyticsSameAs !== 'boolean' || options.jsonData.azureLogAnalyticsSameAs;
}
export function isCredentialsComplete(credentials: AzureCredentials): boolean {
switch (credentials.authType) {
case 'msi':
@@ -116,29 +102,6 @@ export function getCredentials(options: AzureDataSourceSettings): AzureCredentia
}
}
export function getLogAnalyticsCredentials(options: AzureDataSourceSettings): AzureCredentials | undefined {
const authType = getAuthType(options);
if (authType !== 'clientsecret') {
// Only app registration (client secret) authentication supports different credentials for Log Analytics
// for backward compatibility
return undefined;
}
if (isLogAnalyticsSameAs(options)) {
return undefined;
}
return {
authType: 'clientsecret',
azureCloud: options.jsonData.cloudName || getDefaultAzureCloud(),
tenantId: options.jsonData.logAnalyticsTenantId,
clientId: options.jsonData.logAnalyticsClientId,
clientSecret: getLogAnalyticsSecret(options),
defaultSubscriptionId: options.jsonData.logAnalyticsSubscriptionId,
};
}
export function updateCredentials(
options: AzureDataSourceSettings,
credentials: AzureCredentials
@@ -158,12 +121,6 @@ export function updateCredentials(
},
};
if (!isLogAnalyticsSameAs(options)) {
options = updateLogAnalyticsSameAs(options, true);
} else {
options = updateLogAnalyticsCredentials(options, credentials);
}
return options;
case 'clientsecret':
@@ -190,84 +147,10 @@ export function updateCredentials(
},
};
if (isLogAnalyticsSameAs(options)) {
options = updateLogAnalyticsCredentials(options, credentials);
}
return options;
}
}
export function updateLogAnalyticsCredentials(
options: AzureDataSourceSettings,
credentials: AzureCredentials
): AzureDataSourceSettings {
// Log Analytics credentials only used if primary credentials are App Registration (client secret)
if (credentials.authType === 'clientsecret') {
options = {
...options,
jsonData: {
...options.jsonData,
logAnalyticsTenantId: credentials.tenantId,
logAnalyticsClientId: credentials.clientId,
},
secureJsonData: {
...options.secureJsonData,
logAnalyticsClientSecret:
typeof credentials.clientSecret === 'string' && credentials.clientSecret.length > 0
? credentials.clientSecret
: undefined,
},
secureJsonFields: {
...options.secureJsonFields,
logAnalyticsClientSecret: typeof credentials.clientSecret === 'symbol',
},
};
}
// Default subscription
options = {
...options,
jsonData: {
...options.jsonData,
logAnalyticsSubscriptionId: credentials.defaultSubscriptionId,
},
};
return options;
}
export function updateLogAnalyticsSameAs(options: AzureDataSourceSettings, sameAs: boolean): AzureDataSourceSettings {
if (sameAs !== isLogAnalyticsSameAs(options)) {
// Update the 'Same As' switch
options = {
...options,
jsonData: {
...options.jsonData,
azureLogAnalyticsSameAs: sameAs,
},
};
if (sameAs) {
// Get the primary credentials
let credentials = getCredentials(options);
// Check whether the primary client secret is concealed
if (credentials.authType === 'clientsecret' && typeof credentials.clientSecret === 'symbol') {
// Log Analytics credentials need to be synchronized but the client secret is concealed,
// so we have to reset the primary client secret to ensure that user enters a new secret
credentials.clientSecret = undefined;
options = updateCredentials(options, credentials);
}
// Synchronize the Log Analytics credentials with primary credentials
options = updateLogAnalyticsCredentials(options, credentials);
}
}
return options;
}
export function isAppInsightsConfigured(options: AzureDataSourceSettings) {
return !!(options.jsonData.appInsightsAppId && options.secureJsonFields.appInsightsApiKey);
}
@@ -115,74 +115,6 @@
{ "name": "x-ms-app", "content": "Grafana" }
]
},
{
"path": "workspacesloganalytics",
"method": "*",
"url": "https://management.azure.com",
"authType": "azure",
"tokenAuth": {
"scopes": ["https://management.azure.com/.default"],
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureCloud",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
}
},
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
},
{
"path": "chinaworkspacesloganalytics",
"method": "*",
"url": "https://management.chinacloudapi.cn",
"authType": "azure",
"tokenAuth": {
"scopes": ["https://management.chinacloudapi.cn/.default"],
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureChinaCloud",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
}
},
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
},
{
"path": "govworkspacesloganalytics",
"method": "*",
"url": "https://management.usgovcloudapi.net",
"authType": "azure",
"tokenAuth": {
"scopes": ["https://management.usgovcloudapi.net/.default"],
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureUSGovernment",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
}
},
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
},
{
"path": "germanyworkspacesloganalytics",
"method": "*",
"url": "https://management.microsoftazure.de",
"authType": "azure",
"tokenAuth": {
"scopes": ["https://management.microsoftazure.de/.default"],
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureUSGovernment",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
}
},
"headers": [{ "name": "x-ms-app", "content": "Grafana" }]
},
{
"path": "loganalyticsazure",
"method": "GET",
@@ -193,9 +125,9 @@
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureCloud",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
"tenant_id": "{{.JsonData.tenantId | orEmpty}}",
"client_id": "{{.JsonData.clientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.clientSecret | orEmpty}}"
}
},
"headers": [
@@ -213,9 +145,9 @@
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureChinaCloud",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
"tenant_id": "{{.JsonData.tenantId | orEmpty}}",
"client_id": "{{.JsonData.clientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.clientSecret | orEmpty}}"
}
},
"headers": [
@@ -233,9 +165,9 @@
"params": {
"azure_auth_type": "{{.JsonData.azureAuthType | orEmpty}}",
"azure_cloud": "AzureUSGovernment",
"tenant_id": "{{.JsonData.logAnalyticsTenantId | orEmpty}}",
"client_id": "{{.JsonData.logAnalyticsClientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.logAnalyticsClientSecret | orEmpty}}"
"tenant_id": "{{.JsonData.tenantId | orEmpty}}",
"client_id": "{{.JsonData.clientId | orEmpty}}",
"client_secret": "{{.SecureJsonData.clientSecret | orEmpty}}"
}
},
"headers": [
@@ -1,5 +1,5 @@
import { FetchResponse, getBackendSrv } from '@grafana/runtime';
import { getLogAnalyticsManagementApiRoute } from '../api/routes';
import { getManagementApiRoute } from '../api/routes';
import {
locationDisplayNames,
logsSupportedLocationsKusto,
@@ -148,7 +148,7 @@ export default class ResourcePickerData {
try {
return await getBackendSrv()
.fetch<AzureGraphResponse<T>>({
url: this.proxyUrl + '/' + getLogAnalyticsManagementApiRoute(this.cloud) + RESOURCE_GRAPH_URL,
url: this.proxyUrl + '/' + getManagementApiRoute(this.cloud) + RESOURCE_GRAPH_URL,
method: 'POST',
data: {
query: query,
@@ -83,11 +83,15 @@ export interface AzureDataSourceJsonData extends DataSourceJsonData {
subscriptionId?: string;
// logs
azureLogAnalyticsSameAs?: boolean;
logAnalyticsTenantId?: string;
logAnalyticsClientId?: string;
logAnalyticsSubscriptionId?: string;
logAnalyticsDefaultWorkspace?: string;
/** @deprecated Azure Logs credentials */
azureLogAnalyticsSameAs?: boolean;
/** @deprecated Azure Logs credentials */
logAnalyticsTenantId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsClientId?: string;
/** @deprecated Azure Logs credentials */
logAnalyticsSubscriptionId?: string;
// App Insights
appInsightsAppId?: string;
@@ -95,7 +99,6 @@ export interface AzureDataSourceJsonData extends DataSourceJsonData {
export interface AzureDataSourceSecureJsonData {
clientSecret?: string;
logAnalyticsClientSecret?: string;
appInsightsApiKey?: string;
}