44 *--------------------------------------------------------------------------------------------*/
55
66import { type NameValuePair , type Site , type SiteConfig , type WebSiteManagementClient } from '@azure/arm-appservice' ;
7+ import { type Identity } from '@azure/arm-resources' ;
78import { BlobServiceClient } from '@azure/storage-blob' ;
89import { ParsedSite , WebsiteOS , type CustomLocation , type IAppServiceWizardContext } from '@microsoft/vscode-azext-azureappservice' ;
910import { LocationListStep } from '@microsoft/vscode-azext-azureutils' ;
@@ -16,6 +17,7 @@ import { ext } from '../../extensionVariables';
1617import { localize } from '../../localize' ;
1718import { createWebSiteClient } from '../../utils/azureClients' ;
1819import { getRandomHexString } from '../../utils/fs' ;
20+ import { createAzureWebJobsStorageManagedIdentitySettings } from '../../utils/managedIdentityUtils' ;
1921import { nonNullProp } from '../../utils/nonNull' ;
2022import { getStorageConnectionString } from '../appSettings/connectionSettings/getLocalConnectionSetting' ;
2123import { enableFileLogging } from '../logstream/enableFileLogging' ;
@@ -56,7 +58,6 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
5658 context . telemetry . properties . fileLoggingError = maskUserInfo ( parseError ( error ) . message , [ ] ) ;
5759 }
5860 }
59-
6061 showSiteCreated ( site , context ) ;
6162 }
6263
@@ -65,16 +66,8 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
6566 }
6667
6768 private async getNewSite ( context : IFunctionAppWizardContext , stack : FullFunctionAppStack ) : Promise < Site > {
68- const location = await LocationListStep . getLocation ( context , webProvider ) ;
69- const site : Site = {
70- name : context . newSiteName ,
71- kind : getSiteKind ( context ) ,
72- location : nonNullProp ( location , 'name' ) ,
73- serverFarmId : context . plan ?. id ,
74- clientAffinityEnabled : false ,
75- siteConfig : await this . getNewSiteConfig ( context , stack ) ,
76- 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.
77- } ;
69+ const site : Site = await this . createNewSite ( context , stack ) ;
70+ site . 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.
7871
7972 if ( context . customLocation ) {
8073 this . addCustomLocationProperties ( site , context . customLocation ) ;
@@ -99,16 +92,7 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
9992 }
10093
10194 private async getNewFlexSite ( context : IFlexFunctionAppWizardContext , sku : Sku ) : Promise < Site > {
102- const location = await LocationListStep . getLocation ( context , webProvider ) ;
103- const site : Site = {
104- name : context . newSiteName ,
105- kind : getSiteKind ( context ) ,
106- location : nonNullProp ( location , 'name' ) ,
107- serverFarmId : context . plan ?. id ,
108- clientAffinityEnabled : false ,
109- siteConfig : await this . getNewSiteConfig ( context )
110- } ;
111-
95+ const site : Site = await this . createNewSite ( context ) ;
11296 site . functionAppConfig = {
11397 deployment : {
11498 storage : {
@@ -136,16 +120,42 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
136120 return site ;
137121 }
138122
123+ private async createNewSite ( context : IFunctionAppWizardContext , stack ?: FullFunctionAppStack ) : Promise < Site > {
124+ const location = await LocationListStep . getLocation ( context , webProvider ) ;
125+ let identity : Identity | undefined = undefined ;
126+ if ( context . managedIdentity ) {
127+ const userAssignedIdentities = { } ;
128+ userAssignedIdentities [ nonNullProp ( context . managedIdentity , 'id' ) ] =
129+ { principalId : context . managedIdentity ?. principalId , clientId : context . managedIdentity ?. clientId } ;
130+ identity = { type : 'UserAssigned' , userAssignedIdentities }
131+ }
132+
133+ return {
134+ name : context . newSiteName ,
135+ kind : getSiteKind ( context ) ,
136+ location : nonNullProp ( location , 'name' ) ,
137+ serverFarmId : context . plan ?. id ,
138+ clientAffinityEnabled : false ,
139+ siteConfig : await this . getNewSiteConfig ( context , stack ) ,
140+ identity
141+ } ;
142+ }
143+
139144 private async getNewSiteConfig ( context : IFunctionAppWizardContext , stack ?: FullFunctionAppStack ) : Promise < SiteConfig > {
140145 let newSiteConfig : SiteConfig = { } ;
141146
142147 const storageConnectionString : string = ( await getStorageConnectionString ( context ) ) . connectionString ;
143- let appSettings : NameValuePair [ ] = [
144- {
148+
149+ let appSettings : NameValuePair [ ] = [ ] ;
150+ if ( context . managedIdentity ) {
151+ appSettings . push ( ...createAzureWebJobsStorageManagedIdentitySettings ( context ) ) ;
152+ } else {
153+ appSettings . push ( {
145154 name : ConnectionKey . Storage ,
146155 value : storageConnectionString
147- }
148- ] ;
156+ } ) ;
157+ }
158+
149159
150160 if ( stack ) {
151161 const stackSettings : FunctionAppRuntimeSettings = nonNullProp ( stack . minorVersion . stackSettings , context . newSiteOS === WebsiteOS . linux ? 'linuxRuntimeSettings' : 'windowsRuntimeSettings' ) ;
@@ -254,19 +264,25 @@ function getSiteKind(context: IAppServiceWizardContext): string {
254264
255265// storage container is needed for flex deployment, but it is not created automatically
256266async function tryCreateStorageContainer ( site : Site , storageConnectionString : string ) : Promise < void > {
257- const blobClient = BlobServiceClient . fromConnectionString ( storageConnectionString ) ;
258- const containerUrl : string | undefined = site . functionAppConfig ?. deployment ?. storage ?. value ;
259- if ( containerUrl ) {
260- const containerName = containerUrl . split ( '/' ) . pop ( ) ;
261- if ( containerName ) {
262- const client = blobClient . getContainerClient ( containerName ) ;
263- if ( ! await client . exists ( ) ) {
264- await blobClient . createContainer ( containerName ) ;
265- } else {
266- ext . outputChannel . appendLog ( localize ( 'deploymentStorageExists' , 'Deployment storage container "{0}" already exists.' , containerName ) ) ;
267- return ;
267+ try {
268+ const blobClient = BlobServiceClient . fromConnectionString ( storageConnectionString ) ;
269+ const containerUrl : string | undefined = site . functionAppConfig ?. deployment ?. storage ?. value ;
270+ if ( containerUrl ) {
271+ const containerName = containerUrl . split ( '/' ) . pop ( ) ;
272+ if ( containerName ) {
273+ const client = blobClient . getContainerClient ( containerName ) ;
274+ if ( ! await client . exists ( ) ) {
275+ await blobClient . createContainer ( containerName ) ;
276+ } else {
277+ ext . outputChannel . appendLog ( localize ( 'deploymentStorageExists' , 'Deployment storage container "{0}" already exists.' , containerName ) ) ;
278+ return ;
279+ }
268280 }
269281 }
282+ } catch ( error ) {
283+ // ignore error, we will show a warning in the output channel
284+ const parsedError = parseError ( error ) ;
285+ ext . outputChannel . appendLog ( localize ( 'failedToCreateDeploymentStorage' , 'Failed to create deployment storage container. {0}' , parsedError . message ) ) ;
270286 }
271287
272288 ext . outputChannel . appendLog ( localize ( 'noDeploymentStorage' , 'No deployment storage specified in function app.' ) ) ;
0 commit comments