55
66import { type ManagedServiceIdentityClient } from '@azure/arm-msi' ;
77import { 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' ;
1314
1415/**
1516 * 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
2021 *
2122 * @populates `context.managedIdentity`
2223 */
23- export class FunctionAppUserAssignedIdentitiesListStep < T extends IFunctionAppUserAssignedIdentitiesContext > extends AzureWizardPromptStep < T > {
24+ export class FunctionAppUserAssignedIdentitiesListStep < T extends ManagedIdentityAssignContext > extends AzureWizardPromptStep < T > {
2425 private _msiClient : ManagedServiceIdentityClient ;
25- private _hasTargetRole ?: boolean ;
2626
27- constructor ( readonly targetRole ?: Role ) {
27+ constructor (
28+ readonly targetRole ?: Role ,
29+ readonly options ?: { identityAssignStepPriority ?: number } ,
30+ ) {
2831 super ( ) ;
2932 }
3033
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-
3934 // Verify if any of the existing user assigned identities for the function app have the required role already
4035 public async configureBeforePrompt ( context : T ) : Promise < void > {
41- if ( ! this . targetRole || ! this . targetRole ?. scopeId ) {
42- this . _hasTargetRole = undefined ;
36+ if ( ! this . targetRole ?. scopeId ) {
4337 return ;
4438 }
4539
46- this . _hasTargetRole = false ;
4740 this . _msiClient ??= await createManagedServiceIdentityClient ( context ) ;
4841 const amClient = await createAuthorizationManagementClient ( context ) ;
4942
@@ -52,6 +45,7 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
5245 const identityIds : string [ ] = Object . keys ( site . identity ?. userAssignedIdentities ?? { } ) ?? [ ] ;
5346 context . telemetry . properties . functionAppUserAssignedIdentityCount = String ( identityIds . length ) ;
5447
48+ let hasTargetRole : boolean = false ;
5549 for ( const identityId of identityIds ) {
5650 const uaid = site . identity ?. userAssignedIdentities ?. [ identityId ] ;
5751 const roleAssignments = await uiUtils . listAllIterator ( amClient . roleAssignments . listForScope (
@@ -65,20 +59,21 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
6559 if ( roleAssignments . some ( r => ! ! r . roleDefinitionId ?. endsWith ( role . roleDefinitionId ) ) ) {
6660 const parsedIdentity = parseAzureResourceId ( identityId ) ;
6761 context . managedIdentity = await this . _msiClient . userAssignedIdentities . get ( parsedIdentity . resourceGroup , parsedIdentity . resourceName ) ;
68- this . _hasTargetRole = true ;
62+ hasTargetRole = true ;
6963 break ;
7064 }
7165 }
7266
73- context . telemetry . properties . functionAppHasIdentityWithTargetRole = String ( this . hasIdentityWithTargetRole ) ;
67+ context . telemetry . properties . functionAppHasIdentityWithTargetRole = String ( hasTargetRole ) ;
7468
75- if ( this . hasIdentityWithTargetRole ) {
69+ if ( hasTargetRole ) {
7670 prependOrInsertAfterLastInfoChild ( context ,
7771 new ActivityChildItem ( {
72+ stepId : this . id ,
7873 label : localize ( 'useIdentityWithRole' , 'Use identity "{0}" with role "{1}"' , context . managedIdentity ?. name , this . targetRole . roleDefinitionName ) ,
7974 contextValue : createContextValue ( [ 'functionAppUserAssignedIdentitiesListStepItem' , activityInfoContext ] ) ,
8075 activityType : ActivityChildType . Info ,
81- iconPath : activityInfoIcon
76+ iconPath : activityInfoIcon ,
8277 } ) as ActivityInfoChild ,
8378 ) ;
8479 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
8984
9085 public async prompt ( context : T ) : Promise < void > {
9186 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 ) , {
9388 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.' ) ,
9689 } ) ) . data ;
9790
91+ if ( ! identityId ) {
92+ return ;
93+ }
94+
9895 const parsedIdentity : ParsedAzureResourceId = parseAzureResourceId ( identityId ) ;
9996 this . _msiClient ??= await createManagedServiceIdentityClient ( context ) ;
10097
@@ -106,14 +103,34 @@ export class FunctionAppUserAssignedIdentitiesListStep<T extends IFunctionAppUse
106103 return ! context . managedIdentity ;
107104 }
108105
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+ ) ;
118135 }
119136}
0 commit comments