Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions azure/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

/* eslint-disable @typescript-eslint/no-explicit-any */

import { AuthorizationManagementClient, type RoleDefinition } from '@azure/arm-authorization';
import { Identity, ManagedServiceIdentityClient } from '@azure/arm-msi';
import type { AuthorizationManagementClient, RoleDefinition } from '@azure/arm-authorization';
import type { Identity, ManagedServiceIdentityClient } from '@azure/arm-msi';
import type { ExtendedLocation, ResourceGroup } from '@azure/arm-resources';
import type { Location } from '@azure/arm-resources-subscriptions';
import type { StorageAccount } from '@azure/arm-storage';
import { type StorageManagementClient } from '@azure/arm-storage';
import type { StorageManagementClient } from '@azure/arm-storage';
import type { ServiceClient, ServiceClientOptions } from '@azure/core-client';
import type { PagedAsyncIterableIterator } from '@azure/core-paging';
import type { PipelineRequestOptions, PipelineResponse } from '@azure/core-rest-pipeline';
Expand Down
49 changes: 31 additions & 18 deletions azure/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions azure/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@microsoft/vscode-azext-azureutils",
"author": "Microsoft Corporation",
"version": "4.0.1",
"version": "5.0.0",
"description": "Common Azure utils for developing Azure extensions for VS Code",
"tags": [
"azure",
Expand Down Expand Up @@ -39,10 +39,10 @@
"@azure/arm-authorization": "^9.0.0",
"@azure/arm-authorization-profile-2020-09-01-hybrid": "^2.1.0",
"@azure/arm-msi": "^2.2.0",
"@azure/arm-resources": "^5.0.0",
"@azure/arm-resources": "^7.0.0",
"@azure/arm-resources-profile-2020-09-01-hybrid": "^2.1.0",
"@azure/arm-resources-subscriptions": "^2.1.0",
"@azure/arm-storage": "^18.6.0",
"@azure/arm-storage": "^19.1.0",
"@azure/arm-storage-profile-2020-09-01-hybrid": "^2.1.0",
"@azure/core-client": "^1.10.1",
"@azure/core-rest-pipeline": "^1.22.2",
Expand Down
25 changes: 18 additions & 7 deletions azure/src/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,55 @@
*--------------------------------------------------------------------------------------------*/

import type { AuthorizationManagementClient } from '@azure/arm-authorization';
import type { AuthorizationManagementClient as PAMC } from '@azure/arm-authorization-profile-2020-09-01-hybrid';
import type { ManagedServiceIdentityClient } from '@azure/arm-msi';
import type { ResourceManagementClient } from '@azure/arm-resources';
import type { ResourceManagementClient as PRMC } from '@azure/arm-resources-profile-2020-09-01-hybrid';
import type { SubscriptionClient } from '@azure/arm-resources-subscriptions';
import type { StorageManagementClient } from '@azure/arm-storage';
import type { StorageManagementClient as PSMC } from '@azure/arm-storage-profile-2020-09-01-hybrid';
import type { AzExtClientType } from '../index';
import { createAzureClient, createAzureSubscriptionClient, InternalAzExtClientContext, parseClientContext } from './createAzureClient';

export type CommonAuthorizationManagementClient = AuthorizationManagementClient | PAMC;
export type CommonResourcesClient = ResourceManagementClient | PRMC;
export type CommonStorageManagementClient = StorageManagementClient | PSMC;
Comment on lines +17 to +19
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't in index.d.ts, should add them...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just murder index.d.ts: #2194


// Lazy-load @azure packages to improve startup performance.
// NOTE: The client is the only import that matters, the rest of the types disappear when compiled to JavaScript

export async function createStorageClient(context: InternalAzExtClientContext): Promise<StorageManagementClient> {
export async function createStorageClient(context: InternalAzExtClientContext): Promise<CommonStorageManagementClient> {
if (parseClientContext(context).isCustomCloud) {
return <StorageManagementClient><unknown>createAzureClient(context, (await import('@azure/arm-storage-profile-2020-09-01-hybrid')).StorageManagementClient);
return createAzureClient(context, (await import('@azure/arm-storage-profile-2020-09-01-hybrid')).StorageManagementClient);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The casts become unnecessary, by declaring that the return type is a union of the two--that way the callers don't use something that exists on the normal SDK but not on the Azure Stack one.

} else {
return createAzureClient(context, (await import('@azure/arm-storage')).StorageManagementClient as unknown as AzExtClientType<StorageManagementClient>);
}
}

export async function createResourcesClient(context: InternalAzExtClientContext): Promise<ResourceManagementClient> {
export async function createResourcesClient(context: InternalAzExtClientContext): Promise<CommonResourcesClient> {
if (parseClientContext(context).isCustomCloud) {
return <ResourceManagementClient><unknown>createAzureClient(context, (await import('@azure/arm-resources-profile-2020-09-01-hybrid')).ResourceManagementClient);
return createAzureClient(context, (await import('@azure/arm-resources-profile-2020-09-01-hybrid')).ResourceManagementClient);
} else {
return createAzureClient(context, (await import('@azure/arm-resources')).ResourceManagementClient);
return createAzureClient(context, (await import('@azure/arm-resources')).ResourceManagementClient as unknown as AzExtClientType<ResourceManagementClient>);
}
}

export async function createManagedServiceIdentityClient(context: InternalAzExtClientContext): Promise<ManagedServiceIdentityClient> {
return createAzureClient(context, (await import('@azure/arm-msi')).ManagedServiceIdentityClient as unknown as AzExtClientType<ManagedServiceIdentityClient>);
}

export async function createAuthorizationManagementClient(context: InternalAzExtClientContext): Promise<AuthorizationManagementClient> {
export async function createAuthorizationManagementClient(context: InternalAzExtClientContext): Promise<CommonAuthorizationManagementClient> {
if (parseClientContext(context).isCustomCloud) {
return <AuthorizationManagementClient><unknown>createAzureClient(context, (await import('@azure/arm-authorization-profile-2020-09-01-hybrid')).AuthorizationManagementClient);
return createAzureClient(context, (await import('@azure/arm-authorization-profile-2020-09-01-hybrid')).AuthorizationManagementClient);
} else {
return createAzureClient(context, (await import('@azure/arm-authorization')).AuthorizationManagementClient);
}
}

export function isProfileAuthorizationManagementClient(client: CommonAuthorizationManagementClient): client is PAMC {
return !('listForSubscription' in client.roleAssignments);
}

export async function createSubscriptionsClient(context: InternalAzExtClientContext): Promise<SubscriptionClient> {
return createAzureSubscriptionClient(context, (await import('@azure/arm-resources-subscriptions')).SubscriptionClient);
}
7 changes: 6 additions & 1 deletion azure/src/tree/RoleDefinitionsItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { AzExtParentTreeItem, AzExtTreeItem, createGenericElement, createSubscri
import { AzExtResourceType, AzureSubscription, getAzExtResourceType } from "@microsoft/vscode-azureresources-api";
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
import * as types from '../../index';
import { createAuthorizationManagementClient, createSubscriptionsClient } from "../clients";
import { createAuthorizationManagementClient, createSubscriptionsClient, isProfileAuthorizationManagementClient } from "../clients";
import { createPortalUri } from "../utils/createPortalUri";
import { parseAzureResourceGroupId, parseAzureResourceId } from "../utils/parseAzureResourceId";
import { uiUtils } from "../utils/uiUtils";
Expand All @@ -18,6 +18,11 @@ import { getAzureIconPath } from "./IconPath";
export async function createRoleDefinitionsItems(context: IActionContext, subscription: AzureSubscription | ISubscriptionContext, msi: Identity, parentResourceId: string): Promise<RoleDefinitionsItem[]> {
const subContext = isAzureSubscription(subscription) ? createSubscriptionContext(subscription) : subscription;
const authClient = await createAuthorizationManagementClient([context, subContext]);

if (isProfileAuthorizationManagementClient(authClient)) {
throw new Error('TODO: no can do boss');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Do. The Azure Stack SDK does not have the client.roleAssignments.listForSubscription() method.

}

const roleAssignment = await uiUtils.listAllIterator(authClient.roleAssignments.listForSubscription());
// filter the role assignments to only show the ones that are assigned to the msi
const roleAssignments = roleAssignment.filter((ra) => ra.principalId === msi.principalId);
Expand Down
13 changes: 12 additions & 1 deletion azure/src/wizard/RoleAssignmentExecuteStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,18 @@ class SingleRoleAssignmentExecuteStep<T extends types.IResourceGroupWizardContex
const roleDefinitionId = this.role.roleDefinitionId;
const principalId = nonNullValueAndProp(wizardContext.managedIdentity, 'principalId');

await amClient.roleAssignments.create(scope, guid, { roleDefinitionId, principalId });
await amClient.roleAssignments.create(
scope,
guid,
{
roleDefinitionId, // Regular SDK wants this
principalId, // Regular SDK wants this
properties: { // Azure Stack SDK wants this instead
Copy link
Contributor

@MicroFish91 MicroFish91 Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So does this mean the regular SDK doesn't care about having the Stack SDK properties present (in that shape) and vice versa?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope so! I expect that either they won't get serialized into the request or the service will ignore them.

roleDefinitionId,
principalId
},
}
);
} catch (error) {
const parsedError = parseError(error);
const maxRetries = 5;
Expand Down
5 changes: 2 additions & 3 deletions azure/src/wizard/StorageAccountNameStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { StorageManagementClient } from '@azure/arm-storage';
import { AzureNameStep } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
import * as types from '../../index';
import { createStorageClient } from '../clients';
import { createStorageClient, CommonStorageManagementClient } from '../clients';
import { storageProviderType } from '../constants';
import { ResourceGroupListStep, resourceGroupNamingRules } from './ResourceGroupListStep';
import { storageAccountNamingRules } from './StorageAccountListStep';
Expand Down Expand Up @@ -35,7 +34,7 @@ export class StorageAccountNameStep<T extends types.IStorageAccountWizardContext
return await ResourceGroupListStep.isNameAvailable(wizardContext, name);
}

private async validateStorageAccountName(client: StorageManagementClient, name: string): Promise<string | undefined> {
private async validateStorageAccountName(client: CommonStorageManagementClient, name: string): Promise<string | undefined> {
name = name.trim();
if (!name || name.length < storageAccountNamingRules.minLength || name.length > storageAccountNamingRules.maxLength) {
return vscode.l10n.t('The name must be between {0} and {1} characters.', storageAccountNamingRules.minLength, storageAccountNamingRules.maxLength);
Expand Down