Skip to content

Commit ba9bc9b

Browse files
authored
Consume new azure package for target services and role definitions (#1036)
* Consume new azure package for target services and role definitions * Refactor using azure utils package * Merge conflict * Fix package-lock.json * Remove unneeded refres
1 parent d516427 commit ba9bc9b

13 files changed

+179
-326
lines changed

package-lock.json

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@
161161
"category": "Azure",
162162
"icon": "$(zoom-in)"
163163
},
164+
{
165+
"command": "azureResourceGroups.loadAllSubscriptionRoleAssignments",
166+
"title": "%azureResourceGroups.loadAllSubscriptionRoleAssignments%",
167+
"category": "Azure",
168+
"icon": "$(sync)"
169+
},
164170
{
165171
"command": "azureResourceGroups.unfocusGroup",
166172
"title": "%azureResourceGroups.unfocusGroup%",
@@ -493,6 +499,16 @@
493499
}
494500
],
495501
"view/item/context": [
502+
{
503+
"command": "azureResourceGroups.loadAllSubscriptionRoleAssignments",
504+
"when": "view == azureResourceGroups && viewItem =~ /targetServiceRoleAssignmentItem(?!allLoaded)/",
505+
"group": "inline"
506+
},
507+
{
508+
"command": "azureResourceGroups.loadAllSubscriptionRoleAssignments",
509+
"when": "view == azureResourceGroups && viewItem =~ /targetServiceRoleAssignmentItem(?!allLoaded)/",
510+
"group": "1@1"
511+
},
496512
{
497513
"command": "azureResourceGroups.focusGroup",
498514
"when": "view == azureResourceGroups && viewItem =~ /canFocus/",
@@ -862,6 +878,8 @@
862878
"api-extractor": "tsc -p ./api && api-extractor run -c ./api/api-extractor.json"
863879
},
864880
"devDependencies": {
881+
"@azure/arm-authorization": "^9.0.0",
882+
"@azure/arm-msi": "^2.1.0",
865883
"@azure/arm-resources-subscriptions": "^2.1.0",
866884
"@azure/identity": "^4.2.1",
867885
"@microsoft/api-extractor": "^7.33.8",
@@ -894,13 +912,11 @@
894912
"webpack-cli": "^4.6.0"
895913
},
896914
"dependencies": {
897-
"@azure/arm-authorization": "^9.0.0",
898-
"@azure/arm-msi": "^2.1.0",
899915
"@azure/arm-resources": "^5.2.0",
900916
"@azure/arm-resources-profile-2020-09-01-hybrid": "^2.1.0",
901917
"@azure/ms-rest-js": "^2.7.0",
902918
"@microsoft/vscode-azext-azureauth": "^4.0.3",
903-
"@microsoft/vscode-azext-azureutils": "^3.1.1",
919+
"@microsoft/vscode-azext-azureutils": "^3.1.4",
904920
"@microsoft/vscode-azext-utils": "^2.5.12",
905921
"buffer": "^6.0.3",
906922
"form-data": "^4.0.1",

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"azureResourceGroups.showGroupOptions": "Show Grouping Options",
4646
"azureResourceGroups.enableChatStandIn": "Enable the @azure chat stand-in",
4747
"chatParticipants.azure.fullName": "Azure (Preview)",
48+
"azureResourceGroups.loadAllSubscriptionRoleAssignments": "Load all role assignments across subscriptions...",
4849
"azureResourceGroups.askAzure": {
4950
"message": "Ask @azure",
5051
"comment": [

src/commands/registerCommands.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { signInToTenant } from '@microsoft/vscode-azext-azureauth';
7-
import { AzExtTreeItem, IActionContext, isAzExtTreeItem, openUrl, registerCommand, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerReportIssueCommand } from '@microsoft/vscode-azext-utils';
7+
import { AzExtTreeItem, IActionContext, isAzExtTreeItem, nonNullValue, openUrl, registerCommand, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerReportIssueCommand } from '@microsoft/vscode-azext-utils';
88
import { commands } from 'vscode';
99
import { askAgentAboutResource } from '../chat/askAgentAboutResource';
1010
import { askAzureInCommandPalette } from '../chat/askAzure';
1111
import { uploadFileToCloudShell } from '../cloudConsole/uploadFileToCloudShell';
1212
import { ext } from '../extensionVariables';
13-
import { loadAllSubscriptionRoleAssignments } from '../managedIdentity/loadAllSubscriptionRoleAssignments';
13+
import { TargetServiceRoleAssignmentItem } from '../managedIdentity/TargetServiceRoleAssignmentItem';
1414
import { BranchDataItemWrapper } from '../tree/BranchDataItemWrapper';
1515
import { ResourceGroupsItem } from '../tree/ResourceGroupsItem';
1616
import { GroupingItem } from '../tree/azure/grouping/GroupingItem';
@@ -122,9 +122,12 @@ export function registerCommands(): void {
122122
registerCommand('azureResourceGroups.askAzure', askAzureInCommandPalette);
123123

124124
registerCommand('azureWorkspace.loadMore', async (context: IActionContext, node: AzExtTreeItem) => await ext.workspaceTree.loadMore(node, context));
125-
registerCommand('azureResources.loadAllSubscriptionRoleAssignments', loadAllSubscriptionRoleAssignments);
126-
127125
registerCommand('azureTenantsView.configureSovereignCloud', configureSovereignCloud);
126+
registerCommandWithTreeNodeUnwrapping('azureResourceGroups.loadAllSubscriptionRoleAssignments', async (_context: IActionContext, node?: TargetServiceRoleAssignmentItem) => {
127+
node = nonNullValue(node);
128+
node.setAllSubscriptionsLoaded();
129+
ext.azureTreeState.notifyChildrenChanged(node.id);
130+
});
128131
registerCommandWithTreeNodeUnwrapping<{ id?: string }>("azureResourceGroups.askAgentAboutResource", (context, node) => askAgentAboutResource(context, node));
129132
}
130133

src/managedIdentity/ManagedIdentityBranchDataProvider.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,27 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { RoleAssignment } from '@azure/arm-authorization';
7-
import { uiUtils } from '@microsoft/vscode-azext-azureutils';
8-
import { callWithTelemetryAndErrorHandling, createSubscriptionContext, type IActionContext } from '@microsoft/vscode-azext-utils';
6+
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
97
import { AzureResource, AzureResourceModel, BranchDataProvider } from '@microsoft/vscode-azureresources-api';
108
import * as vscode from 'vscode';
119
import { localize } from 'vscode-nls';
1210
import { ext } from '../extensionVariables';
1311
import { ResourceGroupsItem } from '../tree/ResourceGroupsItem';
14-
import { createAuthorizationManagementClient } from '../utils/azureClients';
1512
import { ManagedIdentityItem } from './ManagedIdentityItem';
1613
export class ManagedIdentityBranchDataProvider extends vscode.Disposable implements BranchDataProvider<AzureResource, AzureResourceModel> {
1714
private readonly onDidChangeTreeDataEmitter = new vscode.EventEmitter<ResourceGroupsItem | undefined>();
18-
public roleAssignmentsTask: Promise<{ [id: string]: RoleAssignment[] }>;
1915

2016
constructor() {
2117
super(
2218
() => {
2319
this.onDidChangeTreeDataEmitter.dispose();
2420
});
25-
this.roleAssignmentsTask = this.initialize();
2621
}
2722

2823
get onDidChangeTreeData(): vscode.Event<ResourceGroupsItem | undefined> {
2924
return this.onDidChangeTreeDataEmitter.event;
3025
}
3126

32-
public async initialize(): Promise<{ [id: string]: RoleAssignment[] }> {
33-
return await callWithTelemetryAndErrorHandling('initializeManagedIdentityBranchDataProvider', async (context: IActionContext) => {
34-
const provider = await ext.subscriptionProviderFactory();
35-
const allSubscriptions = await provider.getSubscriptions(false /*filter*/);
36-
const roleAssignments: { [id: string]: RoleAssignment[] } = {};
37-
await Promise.allSettled(allSubscriptions.map(async (subscription) => {
38-
const subContext = createSubscriptionContext(subscription);
39-
const authClient = await createAuthorizationManagementClient([context, subContext]);
40-
roleAssignments[subscription.subscriptionId] = await uiUtils.listAllIterator(authClient.roleAssignments.listForSubscription());
41-
}));
42-
43-
return roleAssignments;
44-
}) ?? {};
45-
}
46-
4727
async getChildren(element: ResourceGroupsItem): Promise<ResourceGroupsItem[] | null | undefined> {
4828
return (await element.getChildren?.())?.map((child) => {
4929
return ext.azureTreeState.wrapItemInStateHandling(child, () => this.refresh(child))

src/managedIdentity/ManagedIdentityItem.ts

Lines changed: 11 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,15 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Identity } from "@azure/arm-msi";
7-
import { uiUtils } from "@microsoft/vscode-azext-azureutils";
7+
import { createManagedServiceIdentityClient } from "@microsoft/vscode-azext-azureutils";
88
import { callWithTelemetryAndErrorHandling, createContextValue, createSubscriptionContext, nonNullProp, type IActionContext } from "@microsoft/vscode-azext-utils";
99
import { type AzureResource, type AzureSubscription, type ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
10-
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
11-
import { createAzureResource } from "../api/DefaultAzureResourceProvider";
10+
import { TreeItem, TreeItemCollapsibleState } from "vscode";
1211
import { getAzureResourcesService } from "../services/AzureResourcesService";
13-
import { DefaultAzureResourceItem } from "../tree/azure/DefaultAzureResourceItem";
14-
import { GenericItem } from "../tree/GenericItem";
1512
import { ResourceGroupsItem } from "../tree/ResourceGroupsItem";
16-
import { createAuthorizationManagementClient, createManagedServiceIdentityClient } from "../utils/azureClients";
1713
import { getIconPath } from "../utils/azureUtils";
18-
import { localize } from "../utils/localize";
19-
import { RoleAssignmentsItem } from "./RoleAssignmentsItem";
20-
import { RoleDefinitionsItem } from "./RoleDefinitionsItem";
14+
import { SourceResourceIdentityItem } from "./SourceResourceIdentityItem";
15+
import { TargetServiceRoleAssignmentItem } from "./TargetServiceRoleAssignmentItem";
2116

2217
export class ManagedIdentityItem implements ResourceGroupsItem {
2318
static readonly contextValue: string = 'managedIdentityItem';
@@ -42,60 +37,28 @@ export class ManagedIdentityItem implements ResourceGroupsItem {
4237
return createContextValue(values);
4338
}
4439

45-
async getChildren(): Promise<(GenericItem | RoleDefinitionsItem | RoleAssignmentsItem)[]> {
40+
async getChildren<TreeElementBase>(): Promise<TreeElementBase[]> {
4641
const result = await callWithTelemetryAndErrorHandling('managedIdentityItem.getChildren', async (context: IActionContext) => {
4742
const subContext = createSubscriptionContext(this.subscription);
4843
const msiClient = await createManagedServiceIdentityClient([context, subContext]);
4944
const msi: Identity = await msiClient.userAssignedIdentities.get(nonNullProp(this.resource, 'resourceGroup'), this.resource.name);
5045

5146
const resources = await getAzureResourcesService().listResources(context, this.subscription);
52-
const assignedRoleAssignment = new RoleAssignmentsItem(localize('sourceResources', 'Source resources'), this.subscription, msi);
53-
const accessRoleAssignment = new RoleAssignmentsItem(localize('targetServices', 'Target services'), this.subscription, msi);
47+
const sourceResourceItem = new SourceResourceIdentityItem(this.subscription, msi, resources);
48+
const targetServiceItem = new TargetServiceRoleAssignmentItem(this.subscription, msi);
5449

55-
const assignedResources = resources.filter((r) => {
56-
// verify the msi is assigned to the resource by checking if the msi id is in the userAssignedIdentities
57-
const userAssignedIdentities = r.identity?.userAssignedIdentities;
58-
if (!userAssignedIdentities) {
59-
return false;
60-
}
61-
62-
if (!msi.id) {
63-
return false;
64-
}
65-
66-
return userAssignedIdentities[msi.id] !== undefined
67-
}).map((r) => {
68-
const azureResoure = createAzureResource(this.subscription, r);
69-
return new DefaultAzureResourceItem(azureResoure);
70-
});
71-
72-
const authClient = await createAuthorizationManagementClient([context, subContext]);
73-
const roleAssignment = await uiUtils.listAllIterator(authClient.roleAssignments.listForSubscription());
74-
// filter the role assignments to only show the ones that are assigned to the msi
75-
const filteredBySub = roleAssignment.filter((ra) => ra.principalId === msi.principalId);
76-
77-
const targetResources = await accessRoleAssignment.getRoleDefinitionsItems(context, filteredBySub);
7850
const children = [];
7951

80-
if (assignedResources.length > 0) {
52+
if (sourceResourceItem.getChildren().length > 0) {
8153
// if there weren't any assigned resources, don't show that section
82-
assignedRoleAssignment.addChildren(assignedResources);
83-
children.push(assignedRoleAssignment);
54+
children.push(sourceResourceItem);
8455
}
8556

86-
accessRoleAssignment.addChildren(targetResources);
87-
children.push(accessRoleAssignment);
88-
accessRoleAssignment.addChild(new GenericItem(localize('showResources', 'Show resources from other subscriptions...'),
89-
{
90-
id: accessRoleAssignment.id + '/showResourcesFromOtherSubscriptions',
91-
iconPath: new ThemeIcon('sync'),
92-
commandId: 'azureResources.loadAllSubscriptionRoleAssignments',
93-
commandArgs: [accessRoleAssignment]
94-
}))
57+
children.push(targetServiceItem);
9558
return children;
9659
});
9760

98-
return result ?? [];
61+
return result as TreeElementBase[] ?? [];
9962
}
10063

10164
getTreeItem(): TreeItem {

0 commit comments

Comments
 (0)