5
5
6
6
import { type ManagedServiceIdentityClient } from '@azure/arm-msi' ;
7
7
import { type ParsedSite } from '@microsoft/vscode-azext-azureappservice' ;
8
- import { createAuthorizationManagementClient , createManagedServiceIdentityClient , parseAzureResourceId , uiUtils , type ParsedAzureResourceId , type Role } from '@microsoft/vscode-azext-azureutils' ;
9
- import { ActivityChildItem , ActivityChildType , activityInfoContext , activityInfoIcon , AzureWizardPromptStep , createContextValue , nonNullProp , prependOrInsertAfterLastInfoChild , type ActivityInfoChild , type IAzureQuickPickItem } from '@microsoft/vscode-azext-utils' ;
10
- import { ext } from '../../../extensionVariables' ;
11
- import { localize } from '../../../localize' ;
12
- import { type IFunctionAppUserAssignedIdentitiesContext } from './IFunctionAppUserAssignedIdentitiesContext' ;
8
+ import { createAuthorizationManagementClient , createManagedServiceIdentityClient , parseAzureResourceId , uiUtils , UserAssignedIdentityListStep , type ParsedAzureResourceId , type Role } from '@microsoft/vscode-azext-azureutils' ;
9
+ import { ActivityChildItem , ActivityChildType , activityInfoContext , activityInfoIcon , AzureWizardPromptStep , createContextValue , nonNullProp , prependOrInsertAfterLastInfoChild , type ActivityInfoChild , type IAzureQuickPickItem , type IWizardOptions } from '@microsoft/vscode-azext-utils' ;
10
+ import { ext } from '../../extensionVariables' ;
11
+ import { localize } from '../../localize' ;
12
+ import { type ManagedIdentityAssignContext } from './ManagedIdentityAssignContext' ;
13
+ import { ManagedIdentityAssignStep } from './ManagedIdentityAssignStep' ;
13
14
14
15
/**
15
16
* Wizard step to select a user-assigned managed identity from the parsed site of a function app.
@@ -20,30 +21,22 @@ import { type IFunctionAppUserAssignedIdentitiesContext } from './IFunctionAppUs
20
21
*
21
22
* @populates `context.managedIdentity`
22
23
*/
23
- export class FunctionAppUserAssignedIdentitiesListStep < T extends IFunctionAppUserAssignedIdentitiesContext > extends AzureWizardPromptStep < T > {
24
+ export class FunctionAppUserAssignedIdentitiesListStep < T extends ManagedIdentityAssignContext > extends AzureWizardPromptStep < T > {
24
25
private _msiClient : ManagedServiceIdentityClient ;
25
- private _hasTargetRole ?: boolean ;
26
26
27
- constructor ( readonly targetRole ?: Role ) {
27
+ constructor (
28
+ readonly targetRole ?: Role ,
29
+ readonly options ?: { identityAssignStepPriority ?: number } ,
30
+ ) {
28
31
super ( ) ;
29
32
}
30
33
31
- /**
32
- * Indicates whether there is at least one user-assigned identity on the function app with the provided role.
33
- * If no role is provided, or if the step has not yet been run, this will return `undefined`.
34
- */
35
- get hasIdentityWithTargetRole ( ) : boolean | undefined {
36
- return this . _hasTargetRole ;
37
- }
38
-
39
34
// Verify if any of the existing user assigned identities for the function app have the required role already
40
35
public async configureBeforePrompt ( context : T ) : Promise < void > {
41
- if ( ! this . targetRole || ! this . targetRole ?. scopeId ) {
42
- this . _hasTargetRole = undefined ;
36
+ if ( ! this . targetRole ?. scopeId ) {
43
37
return ;
44
38
}
45
39
46
- this . _hasTargetRole = false ;
47
40
this . _msiClient ??= await createManagedServiceIdentityClient ( context ) ;
48
41
const amClient = await createAuthorizationManagementClient ( context ) ;
49
42
@@ -52,6 +45,7 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
52
45
const identityIds : string [ ] = Object . keys ( site . identity ?. userAssignedIdentities ?? { } ) ?? [ ] ;
53
46
context . telemetry . properties . functionAppUserAssignedIdentityCount = String ( identityIds . length ) ;
54
47
48
+ let hasTargetRole : boolean = false ;
55
49
for ( const identityId of identityIds ) {
56
50
const uaid = site . identity ?. userAssignedIdentities ?. [ identityId ] ;
57
51
const roleAssignments = await uiUtils . listAllIterator ( amClient . roleAssignments . listForScope (
@@ -65,20 +59,21 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
65
59
if ( roleAssignments . some ( r => ! ! r . roleDefinitionId ?. endsWith ( role . roleDefinitionId ) ) ) {
66
60
const parsedIdentity = parseAzureResourceId ( identityId ) ;
67
61
context . managedIdentity = await this . _msiClient . userAssignedIdentities . get ( parsedIdentity . resourceGroup , parsedIdentity . resourceName ) ;
68
- this . _hasTargetRole = true ;
62
+ hasTargetRole = true ;
69
63
break ;
70
64
}
71
65
}
72
66
73
- context . telemetry . properties . functionAppHasIdentityWithTargetRole = String ( this . hasIdentityWithTargetRole ) ;
67
+ context . telemetry . properties . functionAppHasIdentityWithTargetRole = String ( hasTargetRole ) ;
74
68
75
- if ( this . hasIdentityWithTargetRole ) {
69
+ if ( hasTargetRole ) {
76
70
prependOrInsertAfterLastInfoChild ( context ,
77
71
new ActivityChildItem ( {
72
+ stepId : this . id ,
78
73
label : localize ( 'useIdentityWithRole' , 'Use identity "{0}" with role "{1}"' , context . managedIdentity ?. name , this . targetRole . roleDefinitionName ) ,
79
74
contextValue : createContextValue ( [ 'functionAppUserAssignedIdentitiesListStepItem' , activityInfoContext ] ) ,
80
75
activityType : ActivityChildType . Info ,
81
- iconPath : activityInfoIcon
76
+ iconPath : activityInfoIcon ,
82
77
} ) as ActivityInfoChild ,
83
78
) ;
84
79
ext . outputChannel . appendLog ( localize ( 'foundIdentity' , 'Located existing user assigned identity "{0}" with role "{1}".' , context . managedIdentity ?. name , this . targetRole . roleDefinitionName ) ) ;
@@ -89,12 +84,14 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
89
84
90
85
public async prompt ( context : T ) : Promise < void > {
91
86
const site : ParsedSite = nonNullProp ( context , 'site' ) ;
92
- const identityId : string = ( await context . ui . showQuickPick ( await this . getPicks ( site ) , {
87
+ const identityId : string | undefined = ( await context . ui . showQuickPick ( await this . getPicks ( site ) , {
93
88
placeHolder : localize ( 'selectFunctionAppIdentity' , 'Select a function app identity for new role assignments' ) ,
94
- // Todo: Remove when create + assign is implemented
95
- noPicksMessage : localize ( 'noUserAssignedIdentities' , 'No identities found. Add a user assigned identity to the function app before proceeding.' ) ,
96
89
} ) ) . data ;
97
90
91
+ if ( ! identityId ) {
92
+ return ;
93
+ }
94
+
98
95
const parsedIdentity : ParsedAzureResourceId = parseAzureResourceId ( identityId ) ;
99
96
this . _msiClient ??= await createManagedServiceIdentityClient ( context ) ;
100
97
@@ -106,14 +103,34 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
106
103
return ! context . managedIdentity ;
107
104
}
108
105
109
- private async getPicks ( site : ParsedSite ) : Promise < IAzureQuickPickItem < string > [ ] > {
110
- return Object . keys ( site . identity ?. userAssignedIdentities ?? { } ) . map ( ( id ) => {
111
- const parsedResource : ParsedAzureResourceId = parseAzureResourceId ( id ) ;
112
- return {
113
- label : parsedResource . resourceName ,
114
- description : parsedResource . resourceGroup ,
115
- data : id ,
116
- } ;
117
- } ) ;
106
+ public async getSubWizard ( context : T ) : Promise < IWizardOptions < T > | undefined > {
107
+ if ( context . managedIdentity ) {
108
+ return undefined ;
109
+ }
110
+
111
+ return {
112
+ promptSteps : [ new UserAssignedIdentityListStep ( ) ] ,
113
+ executeSteps : [ new ManagedIdentityAssignStep ( { priority : this . options ?. identityAssignStepPriority } ) ] ,
114
+ } ;
115
+ }
116
+
117
+ private async getPicks ( site : ParsedSite ) : Promise < IAzureQuickPickItem < string | undefined > [ ] > {
118
+ const picks : IAzureQuickPickItem < string | undefined > [ ] = [ {
119
+ label : localize ( 'assignIdentity' , '$(plus) Assign new user-assigned identity' ) ,
120
+ data : undefined ,
121
+ } ] ;
122
+
123
+ return picks . concat (
124
+ Object
125
+ . keys ( site . identity ?. userAssignedIdentities ?? { } )
126
+ . map ( ( id ) => {
127
+ const parsedResource : ParsedAzureResourceId = parseAzureResourceId ( id ) ;
128
+ return {
129
+ label : parsedResource . resourceName ,
130
+ description : parsedResource . resourceGroup ,
131
+ data : id ,
132
+ } ;
133
+ } ) ,
134
+ ) ;
118
135
}
119
136
}
0 commit comments