feat: add assign-role command to manage Key Vault roles for domain certificates

This commit is contained in:
2026-05-22 12:11:58 +02:00
parent 40ec41da28
commit d433569bab
4 changed files with 65 additions and 4 deletions
+42
View File
@@ -1,9 +1,17 @@
#!/usr/bin/env node
import { randomUUID } from 'node:crypto';
import { writeFileSync } from 'node:fs';
import { AuthorizationManagementClient } from '@azure/arm-authorization';
import { DefaultAzureCredential } from '@azure/identity';
import { Command } from 'commander';
import { loadConfig } from './lib/config.js';
import { domainToCertName, Provisioner } from './lib/provisioner.js';
const ROLE_IDS = {
'Key Vault Certificate User': 'db79e9a7-68ee-4b58-9aeb-b90e7c24fcba',
'Key Vault Secrets User': '4633458b-17de-408a-b874-0445c86b69e6',
} as const;
const program = new Command();
program
@@ -31,6 +39,7 @@ function applyOverrides(options: Record<string, unknown>): void {
const sharedOptions = (cmd: Command): Command =>
cmd
.option('--keyvault-url <url>', 'Azure KeyVault URL')
.option('--keyvault-resource-group <rg>', 'Resource group containing the Key Vault')
.option('--subscription-id <id>', 'Azure subscription ID')
.option('--resource-group <rg>', 'Resource group to scan (repeatable)', collect, [])
.option('--dns-zone <zone>', 'Restrict to specific DNS zone (repeatable)', collect, [])
@@ -120,6 +129,39 @@ sharedOptions(
if (result.errors.length > 0) process.exit(1);
});
sharedOptions(
program
.command('assign-role <domain>')
.description('Assign Key Vault Certificate User and Secrets User roles to a principal for a domain certificate')
.requiredOption('--principal-id <id>', 'Azure principal ID to assign roles to')
).action(async (domain: string, options: Record<string, unknown>) => {
applyOverrides(options);
const config = loadConfig();
if (!config.subscriptionId) throw new Error('--subscription-id is required');
if (!config.keyVaultUrl) throw new Error('--keyvault-url is required');
const kvRg = options['keyvaultResourceGroup'];
if (!kvRg) throw new Error('--keyvault-resource-group is required');
const sub = config.subscriptionId;
const principalId = String(options['principalId']);
const vaultName = new URL(config.keyVaultUrl).hostname.split('.')[0];
const certName = domainToCertName(domain);
const vaultBase = `/subscriptions/${sub}/resourceGroups/${kvRg}/providers/Microsoft.KeyVault/vaults/${vaultName}`;
const credential = new DefaultAzureCredential();
const authClient = new AuthorizationManagementClient(credential, sub);
const assignments = [
{ role: 'Key Vault Certificate User' as const, scope: `${vaultBase}/certificates/${certName}` },
{ role: 'Key Vault Secrets User' as const, scope: `${vaultBase}/secrets/${certName}` },
];
for (const { role, scope } of assignments) {
const roleDefinitionId = `/subscriptions/${sub}/providers/Microsoft.Authorization/roleDefinitions/${ROLE_IDS[role]}`;
await authClient.roleAssignments.create(scope, randomUUID(), { roleDefinitionId, principalId });
console.log(`Assigned '${role}' to ${principalId} on ${scope}`);
}
});
sharedOptions(
program
.command('download <domain>')