feat: add assign-role command to manage Key Vault roles for domain certificates
This commit is contained in:
@@ -45,6 +45,7 @@ Commands:
|
||||
status Show certificate expiry status for all managed domains
|
||||
renew Force-renew a certificate for a specific domain
|
||||
download Download the PEM bundle for a domain from Key Vault
|
||||
assign-role Assign Key Vault roles to a principal for a domain certificate
|
||||
|
||||
Common options:
|
||||
--keyvault-url <url> Azure KeyVault URL
|
||||
|
||||
Generated
+19
-2
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"name": "azure-acme-provisioner",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "azure-acme-provisioner",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/arm-authorization": "^9.0.0",
|
||||
"@azure/arm-dns": "^5.1.0",
|
||||
"@azure/functions": "^4.14.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
@@ -73,6 +74,22 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/arm-authorization": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/arm-authorization/-/arm-authorization-9.0.0.tgz",
|
||||
"integrity": "sha512-GdiCA8IA1gO+qcCbFEPj+iLC4+3ByjfKzmeAnkP7MdlL84Yo30Huo/EwbZzwRjYybXYUBuFxGPBB+yeTT4Ebxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-client": "^1.7.0",
|
||||
"@azure/core-paging": "^1.2.0",
|
||||
"@azure/core-rest-pipeline": "^1.8.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/arm-dns": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/arm-dns/-/arm-dns-5.1.0.tgz",
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "azure-acme-provisioner",
|
||||
"version": "0.3.4",
|
||||
"version": "0.4.0",
|
||||
"author": {
|
||||
"name": "Sławomir Koszewski",
|
||||
"url": "https://github.com/skoszewski"
|
||||
@@ -37,6 +37,7 @@
|
||||
"start:function": "func start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/arm-authorization": "^9.0.0",
|
||||
"@azure/arm-dns": "^5.1.0",
|
||||
"@azure/functions": "^4.14.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
|
||||
+42
@@ -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>')
|
||||
|
||||
Reference in New Issue
Block a user