Skip to content

Commit ce1794d

Browse files
committed
First pass core implementation
1 parent ad4745d commit ce1794d

12 files changed

+202
-139
lines changed

src/commands/EXECUTE_PRIORITY.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ When creating or updating resources, execute steps should occupy certain priorit
2828

2929
#### Steps
3030
##### Managed Identity Registry Credential
31-
- Coming soon...
31+
- ManagedEnvironmentIdentityEnableStep: 450
32+
- AcrPullEnableStep: 460
33+
- ManagedIdentityRegistryCredentialAddConfigurationStep: 470
3234

3335
##### Admin User Registry Credential
3436
- AcrEnableAdminUserStep: 450

src/commands/deployWorkspaceProject/getDeployWorkspaceProjectResults.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import { type Workspace } from "@azure/arm-operationalinsights";
88
import { uiUtils } from "@microsoft/vscode-azext-azureutils";
99
import { createOperationalInsightsManagementClient } from "../../utils/azureClients";
1010
import type * as api from "../api/vscode-azurecontainerapps.api";
11-
import { listCredentialsFromRegistry } from "../image/imageSource/containerRegistry/acr/listCredentialsFromRegistry";
11+
import { listCredentialsFromAcr } from "../registryCredentials/dockerLogin/listCredentialsFromAcr";
1212
import { type DeployWorkspaceProjectContext } from "./DeployWorkspaceProjectContext";
1313

1414
export type DeployWorkspaceProjectResults = api.DeployWorkspaceProjectResults;
1515

1616
export async function getDeployWorkspaceProjectResults(context: DeployWorkspaceProjectContext): Promise<DeployWorkspaceProjectResults> {
1717
const registryCredentials: { username: string, password: RegistryPassword } | undefined = context.registry ?
18-
await listCredentialsFromRegistry(context, context.registry) : undefined;
18+
await listCredentialsFromAcr(context) : undefined;
1919

2020
context.logAnalyticsWorkspace ??= await tryGetLogAnalyticsWorkspace(context);
2121

src/commands/image/deployImageApi/deployImage.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { showContainerAppNotification } from "../../createContainerApp/showConta
1414
import { ContainerAppUpdateStep } from "../imageSource/ContainerAppUpdateStep";
1515
import { ImageSourceListStep } from "../imageSource/ImageSourceListStep";
1616
import { type ContainerRegistryImageSourceContext } from "../imageSource/containerRegistry/ContainerRegistryImageSourceContext";
17-
import { RegistryEnableAdminUserStep } from "../imageSource/containerRegistry/acr/RegistryEnableAdminUserStep";
1817
import { type DeployImageApiContext } from "./deployImageApi";
1918

2019
export async function deployImage(context: IActionContext & Partial<ContainerRegistryImageSourceContext>, node: ContainerAppItem): Promise<void> {
@@ -33,7 +32,6 @@ export async function deployImage(context: IActionContext & Partial<ContainerReg
3332
wizardContext.telemetry.properties.revisionMode = containerApp.revisionsMode;
3433

3534
const promptSteps: AzureWizardPromptStep<DeployImageApiContext>[] = [
36-
new RegistryEnableAdminUserStep(),
3735
new ImageSourceListStep(),
3836
new ContainerAppOverwriteConfirmStep(),
3937
];

src/commands/image/imageSource/ContainerAppUpdateStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { type ImageSourceContext } from "./ImageSourceContext";
1313
import { getContainerNameForImage } from "./containerRegistry/getContainerNameForImage";
1414

1515
export class ContainerAppUpdateStep<T extends ImageSourceContext> extends AzureWizardExecuteStep<T> {
16-
public priority: number = 650;
16+
public priority: number = 680;
1717

1818
public async execute(context: T, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
1919
const containerApp: ContainerAppModel = nonNullProp(context, 'containerApp');

src/commands/image/imageSource/containerRegistry/acr/RegistryEnableAdminUserStep.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/commands/image/imageSource/containerRegistry/acr/listCredentialsFromRegistry.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/commands/image/imageSource/containerRegistry/getRegistryCredentialsAndSecrets.ts

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/commands/registryCredentials/RegistryCredentialsAddConfigurationListStep.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { localize } from "../../utils/localize";
1010
import { AcrEnableAdminUserConfirmStep } from "./dockerLogin/AcrEnableAdminUserConfirmStep";
1111
import { AcrEnableAdminUserStep } from "./dockerLogin/AcrEnableAdminUserStep";
1212
import { DockerLoginRegistryCredentialsAddConfigurationStep } from "./dockerLogin/DockerLoginRegistryCredentialsAddConfigurationStep";
13+
import { AcrPullEnableStep } from "./identity/AcrPullEnableStep";
14+
import { ManagedEnvironmentIdentityEnableStep } from "./identity/ManagedEnvironmentIdentityEnableStep";
15+
import { ManagedIdentityRegistryCredentialAddConfigurationStep } from "./identity/ManagedIdentityRegistryCredentialAddConfigurationStep";
1316
import { RegistryCredentialsAndSecretsConfigurationStep } from "./RegistryCredentialsAndSecretsConfigurationStep";
1417
import { type RegistryCredentialsContext } from "./RegistryCredentialsContext";
1518

@@ -56,13 +59,13 @@ export class RegistryCredentialsAddConfigurationListStep extends AzureWizardProm
5659

5760
const registryDomain: SupportedRegistries | undefined = this.getRegistryDomain(context);
5861
switch (context.newRegistryCredentialType) {
59-
// case RegistryCredentialType.SystemAssigned:
60-
// executeSteps.push(
61-
// new ManagedEnvironmentIdentityEnableStep(),
62-
// new AcrPullEnableStep(),
63-
// new ManagedIdentityRegistryCredentialAddConfigurationStep(registryDomain),
64-
// );
65-
// break;
62+
case RegistryCredentialType.SystemAssigned:
63+
executeSteps.push(
64+
new ManagedEnvironmentIdentityEnableStep(),
65+
new AcrPullEnableStep(),
66+
new ManagedIdentityRegistryCredentialAddConfigurationStep(registryDomain),
67+
);
68+
break;
6669
case RegistryCredentialType.DockerLogin:
6770
promptSteps.push(new AcrEnableAdminUserConfirmStep());
6871
executeSteps.push(
@@ -95,12 +98,12 @@ export class RegistryCredentialsAddConfigurationListStep extends AzureWizardProm
9598
const registryDomain = this.getRegistryDomain(context);
9699

97100
if (registryDomain === acrDomain) {
98-
// picks.push({
99-
// label: 'Managed Identity',
100-
// description: '(recommended)',
101-
// detail: localize('systemIdentityDetails', 'Setup "{0}" access for container environment resources via a system-assigned identity', 'acrPull'),
102-
// data: RegistryCredentialType.SystemAssigned,
103-
// });
101+
picks.push({
102+
label: 'Managed Identity',
103+
description: '(recommended)',
104+
detail: localize('systemIdentityDetails', 'Setup "{0}" access for container environment resources via a system-assigned identity', 'acrPull'),
105+
data: RegistryCredentialType.SystemAssigned,
106+
});
104107
}
105108

106109
picks.push({
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { KnownPrincipalType, type AuthorizationManagementClient, type RoleAssignment, type RoleAssignmentCreateParameters } from "@azure/arm-authorization";
7+
import { uiUtils } from "@microsoft/vscode-azext-azureutils";
8+
import { AzureWizardExecuteStep, GenericParentTreeItem, GenericTreeItem, activityFailContext, activityFailIcon, activitySuccessContext, activitySuccessIcon, createUniversallyUniqueContextValue, nonNullValueAndProp, type ExecuteActivityOutput } from "@microsoft/vscode-azext-utils";
9+
import * as crypto from "crypto";
10+
import { type Progress } from "vscode";
11+
import { createAuthorizationManagementClient } from "../../../utils/azureClients";
12+
import { localize } from "../../../utils/localize";
13+
import { type ManagedIdentityRegistryCredentialsContext } from "./ManagedIdentityRegistryCredentialsContext";
14+
15+
const acrPullRoleId: string = '7f951dda-4ed3-4680-a7ca-43fe172d538d';
16+
17+
export class AcrPullEnableStep extends AzureWizardExecuteStep<ManagedIdentityRegistryCredentialsContext> {
18+
public priority: number = 460;
19+
20+
// Add a configureBeforeExecute
21+
22+
public async execute(context: ManagedIdentityRegistryCredentialsContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
23+
const client: AuthorizationManagementClient = await createAuthorizationManagementClient(context);
24+
const registryId: string = nonNullValueAndProp(context.registry, 'id');
25+
const managedEnvironmentIdentity: string = nonNullValueAndProp(context.managedEnvironment?.identity, 'principalId');
26+
27+
if (await this.hasAcrPullAssignment(client, registryId, managedEnvironmentIdentity)) {
28+
return;
29+
}
30+
31+
const roleCreateParams: RoleAssignmentCreateParameters = {
32+
description: 'acr pull',
33+
roleDefinitionId: `/providers/Microsoft.Authorization/roleDefinitions/${acrPullRoleId}`,
34+
principalId: nonNullValueAndProp(context.managedEnvironment?.identity, 'principalId'),
35+
principalType: KnownPrincipalType.ServicePrincipal,
36+
};
37+
38+
progress.report({ message: localize('updatingRegistryCredentials', 'Updating registry credentials...') });
39+
await client.roleAssignments.create(
40+
nonNullValueAndProp(context.registry, 'id'),
41+
crypto.randomUUID(),
42+
roleCreateParams,
43+
);
44+
}
45+
46+
public shouldExecute(context: ManagedIdentityRegistryCredentialsContext): boolean {
47+
return !!context.registry;
48+
}
49+
50+
private async hasAcrPullAssignment(client: AuthorizationManagementClient, registryId: string, managedEnvironmentIdentity: string): Promise<boolean> {
51+
const roleAssignments: RoleAssignment[] = await uiUtils.listAllIterator(client.roleAssignments.listForScope(
52+
registryId,
53+
{
54+
// $filter=principalId eq {id}
55+
filter: `principalId eq '{${managedEnvironmentIdentity}}'`,
56+
}
57+
));
58+
return roleAssignments.some(r => !!r.roleDefinitionId?.endsWith(acrPullRoleId));
59+
}
60+
61+
public createSuccessOutput(): ExecuteActivityOutput {
62+
return {
63+
item: new GenericTreeItem(undefined, {
64+
contextValue: createUniversallyUniqueContextValue(['containerRegistryAcrPullEnableStepSuccessItem', activitySuccessContext]),
65+
label: localize('enableAcrPull', 'Grant "{0}" access to container environment identity', 'acrPull'),
66+
iconPath: activitySuccessIcon
67+
}),
68+
message: localize('enableAcrPullSuccess', 'Successfully granted "{0}" access to container environment identity.', 'acrPull'),
69+
};
70+
}
71+
72+
public createFailOutput(): ExecuteActivityOutput {
73+
return {
74+
item: new GenericParentTreeItem(undefined, {
75+
contextValue: createUniversallyUniqueContextValue(['containerRegistryAcrPullEnableStepFailItem', activityFailContext]),
76+
label: localize('enableAcrPull', 'Grant "{0}" access to container environment identity"', 'acrPull'),
77+
iconPath: activityFailIcon
78+
}),
79+
message: localize('enableAcrPullFail', 'Failed to grant "{0}" access to container environment identity.', 'acrPull'),
80+
};
81+
}
82+
}

0 commit comments

Comments
 (0)