4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import { type NameValuePair , type Site , type SiteConfig , type WebSiteManagementClient } from '@azure/arm-appservice' ;
7
+ import { type Identity } from '@azure/arm-resources' ;
7
8
import { BlobServiceClient } from '@azure/storage-blob' ;
8
9
import { ParsedSite , WebsiteOS , type CustomLocation , type IAppServiceWizardContext } from '@microsoft/vscode-azext-azureappservice' ;
9
10
import { LocationListStep } from '@microsoft/vscode-azext-azureutils' ;
@@ -16,6 +17,7 @@ import { ext } from '../../extensionVariables';
16
17
import { localize } from '../../localize' ;
17
18
import { createWebSiteClient } from '../../utils/azureClients' ;
18
19
import { getRandomHexString } from '../../utils/fs' ;
20
+ import { createAzureWebJobsStorageManagedIdentitySettings } from '../../utils/managedIdentityUtils' ;
19
21
import { nonNullProp } from '../../utils/nonNull' ;
20
22
import { getStorageConnectionString } from '../appSettings/connectionSettings/getLocalConnectionSetting' ;
21
23
import { enableFileLogging } from '../logstream/enableFileLogging' ;
@@ -56,7 +58,6 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
56
58
context . telemetry . properties . fileLoggingError = maskUserInfo ( parseError ( error ) . message , [ ] ) ;
57
59
}
58
60
}
59
-
60
61
showSiteCreated ( site , context ) ;
61
62
}
62
63
@@ -65,16 +66,8 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
65
66
}
66
67
67
68
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.
78
71
79
72
if ( context . customLocation ) {
80
73
this . addCustomLocationProperties ( site , context . customLocation ) ;
@@ -99,16 +92,7 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
99
92
}
100
93
101
94
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 ) ;
112
96
site . functionAppConfig = {
113
97
deployment : {
114
98
storage : {
@@ -136,16 +120,42 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
136
120
return site ;
137
121
}
138
122
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
+
139
144
private async getNewSiteConfig ( context : IFunctionAppWizardContext , stack ?: FullFunctionAppStack ) : Promise < SiteConfig > {
140
145
let newSiteConfig : SiteConfig = { } ;
141
146
142
147
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 ( {
145
154
name : ConnectionKey . Storage ,
146
155
value : storageConnectionString
147
- }
148
- ] ;
156
+ } ) ;
157
+ }
158
+
149
159
150
160
if ( stack ) {
151
161
const stackSettings : FunctionAppRuntimeSettings = nonNullProp ( stack . minorVersion . stackSettings , context . newSiteOS === WebsiteOS . linux ? 'linuxRuntimeSettings' : 'windowsRuntimeSettings' ) ;
@@ -254,19 +264,25 @@ function getSiteKind(context: IAppServiceWizardContext): string {
254
264
255
265
// storage container is needed for flex deployment, but it is not created automatically
256
266
async 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
+ }
268
280
}
269
281
}
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 ) ) ;
270
286
}
271
287
272
288
ext . outputChannel . appendLog ( localize ( 'noDeploymentStorage' , 'No deployment storage specified in function app.' ) ) ;
0 commit comments