diff --git a/.npmrc b/.npmrc index 961823f..da01891 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,2 @@ -registry=https://pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/ +registry=https://pkgs.dev.azure.com/agentaflow/agentbase/_packaging/node/npm/registry/ always-auth=true diff --git a/infra/main.bicep b/infra/main.bicep index 6682985..9ca93f9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -60,6 +60,9 @@ param marketplaceUrl string = 'https://marketplace.agentbase.dev/api/v1' @description('Azure region for PostgreSQL Flexible Server. Defaults to location but can be overridden per-environment when the subscription lacks quota in the primary region.') param postgresLocation string = location +@description('Object ID of the pipeline service principal that seeds Key Vault secrets. Granted Key Vault Secrets Officer so it can write secrets during deployment.') +param pipelineSpOid string = '' + // ---------------------------------------------------------------------------- // Variables — naming & tags // ---------------------------------------------------------------------------- @@ -412,6 +415,23 @@ module aiRbac 'modules/rbac.bicep' = { } } +// Grant the pipeline service principal Key Vault Secrets Officer so it can write +// secrets during deployment (seed-keyvault.sh). Apps use Secrets User (read-only). +var kvSecretsOfficerRoleId = 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7' +resource kvPipelineRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(pipelineSpOid)) { + scope: keyVaultRes + name: guid(keyVaultRes.id, pipelineSpOid, kvSecretsOfficerRoleId) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', kvSecretsOfficerRoleId) + principalId: pipelineSpOid + principalType: 'ServicePrincipal' + } +} + +resource keyVaultRes 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: keyVaultName +} + // ---------------------------------------------------------------------------- // AI service private endpoint (prod only) // Deployed as a separate module after both aiApp and networking to avoid the diff --git a/infra/main.json b/infra/main.json index 58a3770..d5e6672 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.43.8.12551", - "templateHash": "7871493666138067292" + "templateHash": "4930327647787189307" } }, "functions": [ @@ -103,6 +103,20 @@ "metadata": { "description": "Marketplace API base URL the core platform connects to (separate deployment)" } + }, + "postgresLocation": { + "type": "string", + "defaultValue": "[parameters('location')]", + "metadata": { + "description": "Azure region for PostgreSQL Flexible Server. Defaults to location but can be overridden per-environment when the subscription lacks quota in the primary region." + } + }, + "pipelineSpOid": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Object ID of the pipeline service principal that seeds Key Vault secrets. Granted Key Vault Secrets Officer so it can write secrets during deployment." + } } }, "variables": { @@ -127,9 +141,28 @@ "aiAppName": "[format('app-{0}-ai-{1}-{2}', parameters('project'), parameters('environment'), parameters('uniqueSuffix'))]", "coreHost": "[format('{0}.azurewebsites.net', variables('coreAppName'))]", "frontendHost": "[format('{0}.azurewebsites.net', variables('frontendAppName'))]", - "aiHost": "[format('{0}.azurewebsites.net', variables('aiAppName'))]" + "aiHost": "[format('{0}.azurewebsites.net', variables('aiAppName'))]", + "kvSecretsOfficerRoleId": "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" }, "resources": { + "kvPipelineRole": { + "condition": "[not(empty(parameters('pipelineSpOid')))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), parameters('pipelineSpOid'), variables('kvSecretsOfficerRoleId'))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('kvSecretsOfficerRoleId'))]", + "principalId": "[parameters('pipelineSpOid')]", + "principalType": "ServicePrincipal" + } + }, + "keyVaultRes": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[variables('keyVaultName')]" + }, "monitoring": { "type": "Microsoft.Resources/deployments", "apiVersion": "2025-04-01", @@ -621,7 +654,7 @@ "value": "[variables('postgresName')]" }, "location": { - "value": "[parameters('location')]" + "value": "[parameters('postgresLocation')]" }, "tags": { "value": "[variables('tags')]" diff --git a/infra/main.parameters.prod.json b/infra/main.parameters.prod.json index dc536c0..2a0d0a6 100644 --- a/infra/main.parameters.prod.json +++ b/infra/main.parameters.prod.json @@ -7,6 +7,7 @@ "owner": { "value": "platform-team" }, "appServicePlanSku": { "value": "P1v2" }, "deployPrivateNetworking": { "value": true }, - "marketplaceUrl": { "value": "https://marketplace.agentbase.dev/api/v1" } + "marketplaceUrl": { "value": "https://marketplace.agentbase.dev/api/v1" }, + "pipelineSpOid": { "value": "f1030588-91fd-4984-8478-f08a70e743c4" } } } diff --git a/infra/main.parameters.staging.json b/infra/main.parameters.staging.json index 3bc2518..ceea91c 100644 --- a/infra/main.parameters.staging.json +++ b/infra/main.parameters.staging.json @@ -8,6 +8,7 @@ "appServicePlanSku": { "value": "B2" }, "postgresLocation": { "value": "eastus2" }, "deployPrivateNetworking": { "value": false }, - "marketplaceUrl": { "value": "https://marketplace-staging.agentbase.dev/api/v1" } + "marketplaceUrl": { "value": "https://marketplace-staging.agentbase.dev/api/v1" }, + "pipelineSpOid": { "value": "f1030588-91fd-4984-8478-f08a70e743c4" } } }