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

Basic implementation for managed identity for create function app #4213

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 28 additions & 31 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 23 additions & 6 deletions src/commands/createFunctionApp/FunctionAppCreateStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { type NameValuePair, type Site, type SiteConfig, type WebSiteManagementClient } from '@azure/arm-appservice';
import { type Identity } from '@azure/arm-resources';
import { BlobServiceClient } from '@azure/storage-blob';
import { ParsedSite, WebsiteOS, type CustomLocation, type IAppServiceWizardContext } from '@microsoft/vscode-azext-azureappservice';
import { LocationListStep } from '@microsoft/vscode-azext-azureutils';
Expand Down Expand Up @@ -56,7 +57,6 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
context.telemetry.properties.fileLoggingError = parseError(error).message;
}
}

showSiteCreated(site, context);
}

Expand All @@ -66,14 +66,23 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi

private async getNewSite(context: IFunctionAppWizardContext, stack: FullFunctionAppStack): Promise<Site> {
const location = await LocationListStep.getLocation(context, webProvider);
let identity: Identity | undefined = undefined;
if (context.managedIdentity) {
const userAssignedIdentities = {};
userAssignedIdentities[nonNullProp(context.managedIdentity, 'id')] =
{ principalId: context.managedIdentity?.principalId, clientId: context.managedIdentity?.clientId };
identity = { type: 'UserAssigned', userAssignedIdentities }
}

const site: Site = {
name: context.newSiteName,
kind: getSiteKind(context),
location: nonNullProp(location, 'name'),
serverFarmId: context.plan?.id,
clientAffinityEnabled: false,
siteConfig: await this.getNewSiteConfig(context, stack),
reserved: context.newSiteOS === WebsiteOS.linux // The secret property - must be set to true to make it a Linux plan. Confirmed by the team who owns this API.
reserved: context.newSiteOS === WebsiteOS.linux, // The secret property - must be set to true to make it a Linux plan. Confirmed by the team who owns this API.
identity
};

if (context.customLocation) {
Expand Down Expand Up @@ -140,12 +149,20 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
let newSiteConfig: SiteConfig = {};

const storageConnectionString: string = (await getStorageConnectionString(context)).connectionString;
let appSettings: NameValuePair[] = [
{

let appSettings: NameValuePair[] = [];
if (context.managedIdentity) {
appSettings.push({
name: `${ConnectionKey.Storage}__accountName`,
value: context.newStorageAccountName ?? context.storageAccount?.name
Comment on lines +156 to +157
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where a lot of our customer scenarios will break since we relied on that setting for a lot, so still plenty of work to do there.

Can you outline which scenarios you expect to break? Have you verified them breaking?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Deployment: We rely/enforce having the AzureWebJobsStorage setting
  • Debugging: Same thing as above

Those are the main scenarios. We were discussing maybe having two different settings.json files-- one for remote and one for local settings. I'm not actually sure if the AzureWebJobsStorage__accountName setting works locally since you would need an app that has an identity with RBAC to the storage account. That's kind of the main push as to why we'd want to have two separate settings.

});
} else {
appSettings.push({
name: ConnectionKey.Storage,
value: storageConnectionString
}
];
});
}


if (stack) {
const stackSettings: FunctionAppRuntimeSettings = nonNullProp(stack.minorVersion.stackSettings, context.newSiteOS === WebsiteOS.linux ? 'linuxRuntimeSettings' : 'windowsRuntimeSettings');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AppInsightsCreateStep, AppInsightsListStep, AppKind, AppServicePlanCreateStep, AppServicePlanListStep, CustomLocationListStep, LogAnalyticsCreateStep, SiteNameStep, WebsiteOS, type IAppServiceWizardContext } from "@microsoft/vscode-azext-azureappservice";
import { LocationListStep, ResourceGroupCreateStep, ResourceGroupListStep, StorageAccountCreateStep, StorageAccountKind, StorageAccountListStep, StorageAccountPerformance, StorageAccountReplication, type INewStorageAccountDefaults } from "@microsoft/vscode-azext-azureutils";
import { CommonRoleDefinitions, LocationListStep, ResourceGroupCreateStep, ResourceGroupListStep, RoleAssignmentExecuteStep, StorageAccountCreateStep, StorageAccountKind, StorageAccountListStep, StorageAccountPerformance, StorageAccountReplication, UserAssignedIdentityCreateStep, UserAssignedIdentityListStep, type INewStorageAccountDefaults } from "@microsoft/vscode-azext-azureutils";
import { type AzureWizardExecuteStep, type AzureWizardPromptStep, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { FuncVersion, latestGAVersion, tryParseFuncVersion } from "../../FuncVersion";
import { funcVersionSetting } from "../../constants";
Expand Down Expand Up @@ -71,6 +71,7 @@ export async function createCreateFunctionAppComponents(context: ICreateFunction
executeSteps.push(new ResourceGroupCreateStep());
executeSteps.push(new StorageAccountCreateStep(storageAccountCreateOptions));
executeSteps.push(new AppInsightsCreateStep());
executeSteps.push(new UserAssignedIdentityCreateStep());
if (!context.dockerfilePath) {
executeSteps.push(new AppServicePlanCreateStep());
executeSteps.push(new LogAnalyticsCreateStep());
Expand All @@ -94,9 +95,10 @@ export async function createCreateFunctionAppComponents(context: ICreateFunction
}
));
promptSteps.push(new AppInsightsListStep());
promptSteps.push(new UserAssignedIdentityListStep());
}


executeSteps.push(new RoleAssignmentExecuteStep(() => wizardContext?.storageAccount?.id, CommonRoleDefinitions.storageBlobDataContributor));
const storageProvider = 'Microsoft.Storage';
LocationListStep.addProviderForFiltering(wizardContext, storageProvider, 'storageAccounts');

Expand Down