-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add wizard steps to list/create UserAssignedIdentities and execute ro…
…le definitions
- Loading branch information
Showing
9 changed files
with
281 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { AzureWizardExecuteStep, nonNullValueAndProp } from '@microsoft/vscode-azext-utils'; | ||
import { randomUUID } from 'crypto'; | ||
import { l10n, Progress } from 'vscode'; | ||
import * as types from '../../index'; | ||
import { createAuthorizationManagementClient } from '../clients'; | ||
|
||
export enum RoleDefinitionId { | ||
StorageBlobDataContributor = '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' | ||
} | ||
|
||
export class RoleAssignmentExecuteStep<T extends types.IResourceGroupWizardContext> extends AzureWizardExecuteStep<T> { | ||
public priority: number = 900; | ||
private getScopeId: () => string | undefined; | ||
private _roleDefinitionId: types.RoleDefinitionId; | ||
public constructor(getScopeId: () => string | undefined, roleDefinitionId: types.RoleDefinitionId) { | ||
super(); | ||
this.getScopeId = getScopeId; | ||
this._roleDefinitionId = roleDefinitionId; | ||
} | ||
|
||
public async execute(wizardContext: T, _progress: Progress<{ message?: string; increment?: number }>): Promise<void> { | ||
const amClient = await createAuthorizationManagementClient(wizardContext) | ||
const scope = this.getScopeId(); | ||
if (!scope) { | ||
throw new Error(l10n.t('No scope was provided for the role assignment.')); | ||
} | ||
|
||
const guid = randomUUID(); | ||
const roleDefinitionId = this._roleDefinitionId as unknown as string; | ||
const principalId = nonNullValueAndProp(wizardContext.managedIdentity, 'principalId'); | ||
await amClient.roleAssignments.create(scope, guid, { roleDefinitionId, principalId }); | ||
} | ||
|
||
public shouldExecute(wizardContext: T): boolean { | ||
return !!wizardContext.managedIdentity; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { ManagedServiceIdentityClient } from '@azure/arm-msi'; | ||
import { AzureWizardExecuteStep, nonNullValueAndProp } from '@microsoft/vscode-azext-utils'; | ||
import { l10n, Progress } from 'vscode'; | ||
import * as types from '../../index'; | ||
import { createManagedServiceIdentityClient } from '../clients'; | ||
import { storageProvider } from '../constants'; | ||
import { ext } from '../extensionVariables'; | ||
import { LocationListStep } from './LocationListStep'; | ||
|
||
/** | ||
* Naming constraints: | ||
* The resource name must start with a letter or number, | ||
* have a length between 3 and 128 characters and | ||
* can only contain a combination of alphanumeric characters, hyphens and underscores | ||
* But since we are appending "-identities" to the resource group name and that has the same constraints and a 90 character limit, | ||
* we don't need to do any verification | ||
**/ | ||
export class UserAssignedIdentityCreateStep<T extends types.IResourceGroupWizardContext> extends AzureWizardExecuteStep<T> { | ||
public priority: number = 140; | ||
|
||
public constructor() { | ||
super(); | ||
} | ||
|
||
public async execute(wizardContext: T, progress: Progress<{ message?: string; increment?: number }>): Promise<void> { | ||
const newLocation: string = (await LocationListStep.getLocation(wizardContext, storageProvider)).name; | ||
const rgName: string = nonNullValueAndProp(wizardContext.resourceGroup, 'name'); | ||
const newName: string = `${rgName}-identities`; | ||
const creatingUserAssignedIdentity: string = l10n.t('Creating user assigned identity "{0}" in location "{1}""...', newName, newLocation); | ||
ext.outputChannel.appendLog(creatingUserAssignedIdentity); | ||
progress.report({ message: creatingUserAssignedIdentity }); | ||
const msiClient: ManagedServiceIdentityClient = await createManagedServiceIdentityClient(wizardContext); | ||
wizardContext.managedIdentity = await msiClient.userAssignedIdentities.createOrUpdate( | ||
rgName, | ||
newName, | ||
{ | ||
location: newLocation | ||
} | ||
); | ||
const createdUserAssignedIdentity: string = l10n.t('Successfully created user assigned identity "{0}".', newName); | ||
ext.outputChannel.appendLog(createdUserAssignedIdentity); | ||
} | ||
|
||
public shouldExecute(wizardContext: T): boolean { | ||
return !wizardContext.managedIdentity; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { Identity } from '@azure/arm-msi'; | ||
import { AzureWizardPromptStep, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions, nonNullProp } from '@microsoft/vscode-azext-utils'; | ||
import * as vscode from 'vscode'; | ||
import * as types from '../../index'; | ||
import { createManagedServiceIdentityClient } from '../clients'; | ||
import { uiUtils } from '../utils/uiUtils'; | ||
import { LocationListStep } from './LocationListStep'; | ||
import { UserAssignedIdentityCreateStep } from './UserAssignedIdentityCreateStep'; | ||
|
||
export class UserAssignedIdentityListStep<T extends types.IResourceGroupWizardContext> extends AzureWizardPromptStep<T> { | ||
private _suppressCreate: boolean | undefined; | ||
|
||
public constructor(suppressCreate?: boolean) { | ||
super(); | ||
this._suppressCreate = suppressCreate; | ||
} | ||
|
||
public async prompt(wizardContext: T): Promise<void> { | ||
// Cache resource group separately per subscription | ||
const options: IAzureQuickPickOptions = { placeHolder: 'Select a resource group for new resources.', id: `ResourceGroupListStep/${wizardContext.subscriptionId}` }; | ||
wizardContext.managedIdentity = (await wizardContext.ui.showQuickPick(this.getQuickPicks(wizardContext), options)).data; | ||
if (wizardContext.managedIdentity && !LocationListStep.hasLocation(wizardContext)) { | ||
await LocationListStep.setLocation(wizardContext, nonNullProp(wizardContext.managedIdentity, 'location')); | ||
} | ||
} | ||
|
||
public shouldPrompt(wizardContext: T): boolean { | ||
return !wizardContext.managedIdentity; | ||
} | ||
|
||
public async getSubWizard(wizardContext: T): Promise<IWizardOptions<T> | undefined> { | ||
if (!wizardContext.managedIdentity) { | ||
return { | ||
executeSteps: [new UserAssignedIdentityCreateStep()] | ||
} | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
private async getQuickPicks(wizardContext: T): Promise<IAzureQuickPickItem<Identity | undefined>[]> { | ||
const picks: IAzureQuickPickItem<Identity | undefined>[] = []; | ||
const miClient = await createManagedServiceIdentityClient(wizardContext); | ||
const uai = await uiUtils.listAllIterator(miClient.userAssignedIdentities.listBySubscription()); | ||
|
||
if (!this._suppressCreate) { | ||
picks.push({ | ||
label: vscode.l10n.t('$(plus) Create new user assigned identity'), | ||
description: '', | ||
data: undefined | ||
}); | ||
} | ||
|
||
return picks.concat(uai.map((i: Identity) => { | ||
return { | ||
id: i.id, | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
label: i.name!, | ||
description: i.location, | ||
data: i | ||
}; | ||
})); | ||
} | ||
} |