Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create AI ARM 2.1 for Functions and App Service #1200

Merged
merged 23 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ae9d0dc
Workaround for checkNameAvailability bug for site and slots
nturinski Mar 8, 2022
6c41179
Merge main
nturinski Apr 25, 2022
dce8def
Merge main
nturinski May 3, 2022
693e6af
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski May 6, 2022
8e1b77a
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski May 13, 2022
edffce8
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski May 16, 2022
4ab7a74
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski May 20, 2022
e94dbfd
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski May 20, 2022
7edafc1
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski Jun 27, 2022
de4d5ed
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski Jun 29, 2022
0684d29
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski Jun 29, 2022
45a74d7
Merge branch 'main' of /~https://github.com/microsoft/vscode-azuretools
nturinski Jul 17, 2022
7fafe84
Create log analytics workspace to use for AI 2.1
nturinski Jul 20, 2022
ddf04e5
Update appservice/src/utils/azureClients.ts
nturinski Jul 20, 2022
87614e1
Update appservice/src/createAppService/IAppServiceWizardContext.ts
nturinski Jul 20, 2022
744d31f
Update appservice/src/createAppService/LogAnalyticsCreateStep.ts
nturinski Jul 20, 2022
7f9bcfb
Update appservice/src/createAppService/LogAnalyticsCreateStep.ts
nturinski Jul 20, 2022
82b5f16
Refactor out getAppInsightsSupprotedLocation and use it in LogAnalyti…
nturinski Jul 20, 2022
f9a65f5
Bump version
nturinski Jul 20, 2022
87458e4
Minor fixes
nturinski Jul 20, 2022
c2faa45
PR feedback
nturinski Jul 21, 2022
0bb2ef1
Bump. Sorry for dismissing your review, Brandon
nturinski Jul 21, 2022
4c80f16
Merge main
nturinski Jul 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
431 changes: 286 additions & 145 deletions appservice/package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions appservice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@
},
"dependencies": {
"@azure/abort-controller": "^1.0.4",
"@azure/arm-appinsights": "^4.0.0",
"@azure/arm-appinsights": "^5.0.0-beta.4",
"@azure/arm-appservice": "^11.0.0",
"@azure/arm-operationalinsights": "^8.0.1",
"@azure/arm-resourcegraph": "^4.0.0",
"@azure/arm-resources": "^5.0.0",
"@azure/arm-subscriptions": "^5.0.0",
"@azure/ms-rest-azure-env": "^2.0.0",
"@azure/ms-rest-js": "^2.3.0",
"@azure/storage-blob": "^12.3.0",
"@microsoft/vscode-azext-azureutils": "^0.3.0",
"@microsoft/vscode-azext-utils": "^0.3.3",
"@microsoft/vscode-azext-azureutils": "^0.3.4",
"@microsoft/vscode-azext-utils": "^0.3.9",
"dayjs": "^1.11.2",
"fs-extra": "^10.0.0",
"glob-gitignore": "^1.0.14",
Expand Down
11 changes: 6 additions & 5 deletions appservice/src/createAppService/AppInsightsCreateStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class AppInsightsCreateStep extends AzureWizardExecuteStep<IAppServiceWiz
const resourceLocation: AzExtLocation = await LocationListStep.getLocation(context);
const verifyingAppInsightsAvailable: string = localize('verifyingAppInsightsAvailable', 'Verifying that Application Insights is available for this location...');
ext.outputChannel.appendLog(verifyingAppInsightsAvailable);
const appInsightsLocation: string | undefined = await this.getSupportedLocation(context, resourceLocation);
const appInsightsLocation: string | undefined = await AppInsightsCreateStep.getSupportedLocation(this, context, resourceLocation);

if (appInsightsLocation) {
const client: ApplicationInsightsManagementClient = await createAppInsightsClient(context);
Expand All @@ -42,7 +42,8 @@ export class AppInsightsCreateStep extends AzureWizardExecuteStep<IAppServiceWiz
ext.outputChannel.appendLog(creatingNewAppInsights);
progress.report({ message: creatingNewAppInsights });

context.appInsightsComponent = await client.components.createOrUpdate(rgName, aiName, { kind: 'web', applicationType: 'web', location: appInsightsLocation });
context.appInsightsComponent = await client.components.createOrUpdate(rgName, aiName, { kind: 'web', applicationType: 'web', location: appInsightsLocation,
workspaceResourceId: context.logAnalyticsWorkspace?.id});
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
const createdNewAppInsights: string = localize('createdNewAppInsights', 'Successfully created Application Insights resource "{0}".', aiName);
ext.outputChannel.appendLog(createdNewAppInsights);
} else if (pError.errorType === 'AuthorizationFailed') {
Expand Down Expand Up @@ -84,16 +85,16 @@ export class AppInsightsCreateStep extends AzureWizardExecuteStep<IAppServiceWiz
}

// returns the supported location, a location in the region map, or undefined
private async getSupportedLocation(context: IAppServiceWizardContext, location: AzExtLocation): Promise<string | undefined> {
const locations: string[] = await this.getLocations(context) || [];
public static async getSupportedLocation(step: AppInsightsCreateStep, context: IAppServiceWizardContext, location: AzExtLocation): Promise<string | undefined> {
const locations: string[] = await step.getLocations(context) || [];
const locationName: string = nonNullProp(location, 'name');

if (locations.some((loc) => areLocationNamesEqual(loc, location.name))) {
context.telemetry.properties.aiLocationSupported = 'true';
return locationName;
} else {
// If there is no exact match, then query the regionMapping.json
const pairedRegions: string[] | undefined = await this.getPairedRegions(context, locationName);
const pairedRegions: string[] | undefined = await step.getPairedRegions(context, locationName);
if (pairedRegions.length > 0) {
// if there is at least one region listed, return the first
context.telemetry.properties.aiLocationSupported = 'pairedRegion';
Expand Down
14 changes: 7 additions & 7 deletions appservice/src/createAppService/AppInsightsListStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/

import type { ApplicationInsightsManagementClient } from "@azure/arm-appinsights";
// eslint-disable-next-line import/no-internal-modules
import type { ApplicationInsightsComponent, ApplicationInsightsComponentListResult } from "@azure/arm-appinsights/esm/models";
import { LocationListStep } from "@microsoft/vscode-azext-azureutils";
import type { ApplicationInsightsComponent } from "@azure/arm-appinsights";
import { LocationListStep, uiUtils } from "@microsoft/vscode-azext-azureutils";
import { AzureWizardPromptStep, IAzureNamingRules, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions, nonNullProp } from "@microsoft/vscode-azext-utils";
import { localize } from "../localize";
import { createAppInsightsClient } from "../utils/azureClients";
import { AppInsightsCreateStep } from "./AppInsightsCreateStep";
import { AppInsightsNameStep } from "./AppInsightsNameStep";
import { IAppServiceWizardContext } from "./IAppServiceWizardContext";
import { LogAnalyticsCreateStep } from "./LogAnalyticsCreateStep";

export const appInsightsNamingRules: IAzureNamingRules = {
minLength: 1,
Expand All @@ -30,10 +30,10 @@ export class AppInsightsListStep extends AzureWizardPromptStep<IAppServiceWizard
this._suppressCreate = suppressCreate;
}

public static async getAppInsightsComponents(context: IAppServiceWizardContext): Promise<ApplicationInsightsComponentListResult> {
public static async getAppInsightsComponents(context: IAppServiceWizardContext): Promise<ApplicationInsightsComponent[]> {
if (context.appInsightsTask === undefined) {
const client: ApplicationInsightsManagementClient = await createAppInsightsClient(context);
context.appInsightsTask = client.components.list();
context.appInsightsTask = uiUtils.listAllIterator(client.components.list());
}

return await context.appInsightsTask;
Expand Down Expand Up @@ -65,7 +65,7 @@ export class AppInsightsListStep extends AzureWizardPromptStep<IAppServiceWizard
LocationListStep.addStep(context, promptSteps);
return {
promptSteps: promptSteps,
executeSteps: [new AppInsightsCreateStep()]
executeSteps: [new LogAnalyticsCreateStep(), new AppInsightsCreateStep()]
};
}

Expand All @@ -84,7 +84,7 @@ export class AppInsightsListStep extends AzureWizardPromptStep<IAppServiceWizard
data: undefined
});

let components: ApplicationInsightsComponentListResult = await AppInsightsListStep.getAppInsightsComponents(context);
let components: ApplicationInsightsComponent[] = await AppInsightsListStep.getAppInsightsComponents(context);

// /~https://github.com/microsoft/vscode-azurefunctions/issues/1454
if (!Array.isArray(components)) {
Expand Down
5 changes: 2 additions & 3 deletions appservice/src/createAppService/AppInsightsNameStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// eslint-disable-next-line import/no-internal-modules
import type { ApplicationInsightsComponent, ApplicationInsightsComponentListResult } from '@azure/arm-appinsights/esm/models';
import type { ApplicationInsightsComponent } from '@azure/arm-appinsights';
import { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils';
import { localize } from '../localize';
import { AppInsightsListStep, appInsightsNamingRules } from './AppInsightsListStep';
Expand All @@ -13,7 +12,7 @@ import { IAppServiceWizardContext } from './IAppServiceWizardContext';
export class AppInsightsNameStep extends AzureWizardPromptStep<IAppServiceWizardContext> {

public async isNameAvailable(context: IAppServiceWizardContext, name: string): Promise<boolean> {
const appInsightsComponents: ApplicationInsightsComponentListResult = await AppInsightsListStep.getAppInsightsComponents(context);
const appInsightsComponents: ApplicationInsightsComponent[] = await AppInsightsListStep.getAppInsightsComponents(context);
return !appInsightsComponents.some((ai: ApplicationInsightsComponent) => ai.name !== undefined && ai.name.toLowerCase() === name.toLowerCase());
}

Expand Down
14 changes: 11 additions & 3 deletions appservice/src/createAppService/IAppServiceWizardContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { ApplicationInsightsManagementModels } from '@azure/arm-appinsights';
import { ApplicationInsightsComponent } from '@azure/arm-appinsights';
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
import type { AppServicePlan, Site, SkuDescription } from '@azure/arm-appservice';
import { Workspace } from '@azure/arm-operationalinsights';
nturinski marked this conversation as resolved.
Show resolved Hide resolved
import { IResourceGroupWizardContext, IStorageAccountWizardContext } from '@microsoft/vscode-azext-azureutils';
import { AppKind, WebsiteOS } from './AppKind';

Expand Down Expand Up @@ -69,13 +70,13 @@ export interface IAppServiceWizardContext extends IResourceGroupWizardContext, I
* App Insights components are necessary for Function apps log streaming. By default, we should instantiate
* one for the user if there is a data farm available within the same region as the web app
*/
appInsightsComponent?: ApplicationInsightsManagementModels.ApplicationInsightsComponent;
appInsightsComponent?: ApplicationInsightsComponent;

/**
* The task used to get existing App Insights components.
* By specifying this in the context, we can ensure that Azure is only queried once for the entire wizard
*/
appInsightsTask?: Promise<ApplicationInsightsManagementModels.ApplicationInsightsComponentListResult>;
appInsightsTask?: Promise<ApplicationInsightsComponent[]>;

/**
* Boolean indicating that the user opted out of creating an Application inisghts component.
Expand All @@ -89,6 +90,13 @@ export interface IAppServiceWizardContext extends IResourceGroupWizardContext, I
*/
newAppInsightsName?: string;

/**
* Log Anayltics Workspace component is neccessary for the new App Insights components. By default,
* it will look for a workspace within the same resource group and location as the App Insight
* component. If neither conditions are met, then it will automatically create a workspace
*/
logAnalyticsWorkspace?: Workspace;

/**
* Indicates advanced creation should be used
*/
Expand Down
57 changes: 57 additions & 0 deletions appservice/src/createAppService/LogAnalyticsCreateStep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { AzExtLocation, getResourceGroupFromId, LocationListStep, uiUtils } from "@microsoft/vscode-azext-azureutils";
import { AzureWizardExecuteStep, nonNullProp, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
import { Progress } from "vscode";
import { ext } from "../extensionVariables";
import { localize } from "../localize";
import { createOperationalInsightsManagementClient } from "../utils/azureClients";
import { AppInsightsCreateStep } from "./AppInsightsCreateStep";
import { IAppServiceWizardContext } from "./IAppServiceWizardContext";

export class LogAnalyticsCreateStep extends AzureWizardExecuteStep<IAppServiceWizardContext> {
public priority: number = 134;

public async execute(context: IAppServiceWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
const opClient = await createOperationalInsightsManagementClient(context);
const rgName = nonNullValueAndProp(context.resourceGroup, 'name');
const resourceLocation: AzExtLocation = await LocationListStep.getLocation(context);
const aiStep = new AppInsightsCreateStep();
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
const appInsightsLocation: string | undefined = await AppInsightsCreateStep.getSupportedLocation(aiStep, context, resourceLocation);

if (!appInsightsLocation) {
// if there is no supported AI location, then skip this as AppInsightsCreateStep will be skipped
return undefined;
nturinski marked this conversation as resolved.
Show resolved Hide resolved
}

const workspaces = await uiUtils.listAllIterator(opClient.workspaces.list());
const workspacesInSameLoc = workspaces.filter(ws => ws.location === appInsightsLocation);
const workspacesInSameRg = workspacesInSameLoc.filter(ws => getResourceGroupFromId(nonNullProp(ws, 'id')) === rgName);

context.logAnalyticsWorkspace = workspacesInSameRg[0] ?? workspacesInSameLoc[0];

if (context.logAnalyticsWorkspace) {
const usingLaw: string = localize('usingLogAnalyticsWorkspace', 'Using existing Log Analytics workspace "{0}"', context.logAnalyticsWorkspace.name);
progress.report({ message: usingLaw });
ext.outputChannel.appendLog(usingLaw);
} else {
const creatingLaw: string = localize('creatingLogAnalyticsWorkspace', 'Creating new Log Analytics workspace...');
progress.report({ message: creatingLaw });
ext.outputChannel.appendLog(creatingLaw);

const workspaceName = `workspace-${context.newAppInsightsName}`
context.logAnalyticsWorkspace = await opClient.workspaces.beginCreateOrUpdateAndWait(rgName, workspaceName, { location: appInsightsLocation });

const createdLaw: string = localize('createdLogAnalyticWorkspace', 'Successfully created new Log Analytic workspace "{0}".',workspaceName );
nturinski marked this conversation as resolved.
Show resolved Hide resolved
ext.outputChannel.appendLog(createdLaw);
void progress.report({ message: createdLaw });
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
}
}

public shouldExecute(context: IAppServiceWizardContext): boolean {
return !context.logAnalyticsWorkspace;
}
}
1 change: 1 addition & 0 deletions appservice/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './createAppService/AppServicePlanListStep';
export * from './createAppService/AppServicePlanSkuStep';
export * from './createAppService/CustomLocationListStep';
export * from './createAppService/IAppServiceWizardContext';
export * from './createAppService/LogAnalyticsCreateStep';
export * from './createAppService/setLocationsTask';
export * from './createAppService/SiteNameStep';
export * from './createAppService/SiteOSStep';
Expand Down
5 changes: 5 additions & 0 deletions appservice/src/utils/azureClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import type { ApplicationInsightsManagementClient } from '@azure/arm-appinsights';
import type { WebSiteManagementClient } from '@azure/arm-appservice';
import { OperationalInsightsManagementClient } from '@azure/arm-operationalinsights';
nturinski marked this conversation as resolved.
Show resolved Hide resolved
import type { ResourceGraphClient } from '@azure/arm-resourcegraph';
import type { ResourceManagementClient } from '@azure/arm-resources';
import { AzExtClientContext, createAzureClient, createAzureSubscriptionClient } from '@microsoft/vscode-azext-azureutils';
Expand All @@ -27,3 +28,7 @@ export async function createResourceClient(context: AzExtClientContext): Promise
export async function createResourceGraphClient(context: AzExtClientContext): Promise<ResourceGraphClient> {
return createAzureSubscriptionClient(context, (await import('@azure/arm-resourcegraph')).ResourceGraphClient);
}

export async function createOperationalInsightsManagementClient(context: AzExtClientContext): Promise<OperationalInsightsManagementClient> {
return createAzureClient(context, (await import('@azure/arm-operationalinsights')).OperationalInsightsManagementClient);
}