diff --git a/deploy/standard/bicep/modules/service.bicep b/deploy/standard/bicep/modules/service.bicep index a04a76003a..01811b98ae 100644 --- a/deploy/standard/bicep/modules/service.bicep +++ b/deploy/standard/bicep/modules/service.bicep @@ -1,132 +1,145 @@ -/** Inputs **/ -@description('API Key for service') -@secure() -param apiKey string = newGuid() - -@description('Entra authentication client secret') -@secure() -param clientSecret string = '' - -@description('Location for all resources') -param location string - -@description('Target Kubernetes namespace for microservice deployment') -param namespace string - -@description('OIDC Issuer URL') -param oidcIssuerUrl string - -@description('OPS Resource Group name') -param opsResourceGroupName string - -@description('OPS resource suffix') -param opsResourceSuffix string - -@description('Resource suffix for all resources') -param resourceSuffix string - -@description('Service name') -param serviceName string - -@description('Storage Resource Group name') -param storageResourceGroupName string - -@description('Tags for all resources') -param tags object - -@description('Timestamp for nested deployments') -param timestamp string = utcNow() - -@description('Flag enabling OIDC support.') -param useOidc bool = false - -/** Locals **/ -@description('Formatted untruncated resource name') -var kvFormattedName = toLower('${kvServiceType}-${substring(opsResourceSuffix, 0, length(opsResourceSuffix) - 4)}') - -@description('The Resource Name') -var kvTruncatedName = substring(kvFormattedName,0,min([length(kvFormattedName),20])) -var kvName = '${kvTruncatedName}-${substring(opsResourceSuffix, length(opsResourceSuffix) - 3, 3)}' - -@description('The Resource Service Type token') -var kvServiceType = 'kv' - -/** Outputs **/ -@description('Service Managed Identity Client Id.') -output serviceClientId string = managedIdentity.properties.clientId - -@description('Service Api Key Secret KeyVault Uri.') -#disable-next-line outputs-should-not-contain-secrets -output serviceApiKeySecretUri string = useOidc ? '' : apiKeySecret.outputs.secretUri - -@description('Service OIDC Client Secret KeyVault Uri') -#disable-next-line outputs-should-not-contain-secrets -output serviceApiClientSecretUri string = useOidc ? apiClientSecret.outputs.secretUri : '' - -/** Resources **/ -@description('Resource for configuring user managed identity for a microservice') -resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - location: location - name: 'mi-${serviceName}-${resourceSuffix}' - tags: tags -} - -@description('OIDC Federated Identity Credential for managed identity for a microservice') -resource federatedIdentityCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = { - name: serviceName - parent: managedIdentity - properties: { - audiences: [ 'api://AzureADTokenExchange' ] - issuer: oidcIssuerUrl - subject: 'system:serviceaccount:${namespace}:${serviceName}' - } -} - -@description('OPS Role assignments for microservice managed identity') -module opsRoleAssignments 'utility/roleAssignments.bicep' = { - name: 'opsIAM-${serviceName}-${timestamp}' - scope: resourceGroup(opsResourceGroupName) - params: { - principalId: managedIdentity.properties.principalId - roleDefinitionIds: { - 'App Configuration Data Reader': '516239f1-63e1-4d78-a4de-a74fb236a071' - 'Key Vault Secrets User': '4633458b-17de-408a-b874-0445c86b69e6' - } - } -} - -@description('Storage Role assignments for microservice managed identity') -module storageRoleAssignments 'utility/roleAssignments.bicep' = { - name: 'storageIAM-${serviceName}-${timestamp}' - scope: resourceGroup(storageResourceGroupName) - params: { - principalId: managedIdentity.properties.principalId - roleDefinitionIds: { - Contributor: 'b24988ac-6180-42a0-ab88-20f7382dd24c' - } - } -} - -@description('API Key for microservice (only created if not using Entra)') -module apiKeySecret 'kvSecret.bicep' = if (!useOidc) { - name: 'apiKey-${serviceName}-${timestamp}' - scope: resourceGroup(opsResourceGroupName) - params: { - kvName: kvName - secretName: 'foundationallm-apis-${serviceName}-apikey' - secretValue: useOidc ? '' : apiKey - tags: tags - } -} - -@description('Client secret for microservice (only created if using Entra)') -module apiClientSecret 'kvSecret.bicep' = if (useOidc) { - name: 'apiClientSecret-${serviceName}-${timestamp}' - scope: resourceGroup(opsResourceGroupName) - params: { - kvName: kvName - secretName: 'foundationallm-apis-${serviceName}-entra-clientsecret' - secretValue: useOidc ? clientSecret : '' - tags: tags - } -} +/** Inputs **/ +@description('API Key for service') +@secure() +param apiKey string = newGuid() + +@description('Entra authentication client secret') +@secure() +param clientSecret string = '' + +@description('Location for all resources') +param location string + +@description('Target Kubernetes namespace for microservice deployment') +param namespace string + +@description('OIDC Issuer URL') +param oidcIssuerUrl string + +@description('OPS Resource Group name') +param opsResourceGroupName string + +@description('OPS resource suffix') +param opsResourceSuffix string + +@description('Resource suffix for all resources') +param resourceSuffix string + +@description('Service name') +param serviceName string + +@description('Storage Resource Group name') +param storageResourceGroupName string + +@description('Tags for all resources') +param tags object + +@description('Timestamp for nested deployments') +param timestamp string = utcNow() + +@description('Flag enabling OIDC support.') +param useOidc bool = false + +/** Locals **/ +@description('Formatted untruncated resource name') +var kvFormattedName = toLower('${kvServiceType}-${substring(opsResourceSuffix, 0, length(opsResourceSuffix) - 4)}') + +@description('The Resource Name') +var kvTruncatedName = substring(kvFormattedName,0,min([length(kvFormattedName),20])) +var kvName = '${kvTruncatedName}-${substring(opsResourceSuffix, length(opsResourceSuffix) - 3, 3)}' + +@description('The Resource Service Type token') +var kvServiceType = 'kv' + +/** Outputs **/ +@description('Service Managed Identity Client Id.') +output serviceClientId string = managedIdentity.properties.clientId + +@description('Service Api Key Secret KeyVault Uri.') +#disable-next-line outputs-should-not-contain-secrets +output serviceApiKeySecretUri string = useOidc ? '' : apiKeySecret.outputs.secretUri + +@description('Service OIDC Client Secret KeyVault Uri') +#disable-next-line outputs-should-not-contain-secrets +output serviceApiClientSecretUri string = useOidc ? apiClientSecret.outputs.secretUri : '' + +/** Resources **/ +@description('Resource for configuring user managed identity for a microservice') +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + location: location + name: 'mi-${serviceName}-${resourceSuffix}' + tags: tags +} + +@description('OIDC Federated Identity Credential for managed identity for a microservice') +resource federatedIdentityCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = { + name: serviceName + parent: managedIdentity + properties: { + audiences: [ 'api://AzureADTokenExchange' ] + issuer: oidcIssuerUrl + subject: 'system:serviceaccount:${namespace}:${serviceName}' + } +} + +@description('OPS Role assignments for microservice managed identity') +module opsRoleAssignments 'utility/roleAssignments.bicep' = { + name: 'opsIAM-${serviceName}-${timestamp}' + scope: resourceGroup(opsResourceGroupName) + params: { + principalId: managedIdentity.properties.principalId + roleDefinitionIds: { + 'App Configuration Data Reader': '516239f1-63e1-4d78-a4de-a74fb236a071' + 'Key Vault Secrets User': '4633458b-17de-408a-b874-0445c86b69e6' + } + } +} + +@description('OPS Role assignments for microservice managed identity') +module appRoleAssignments 'utility/roleAssignments.bicep' = { + name: 'appIAM-${serviceName}-${timestamp}' + scope: resourceGroup() + params: { + principalId: managedIdentity.properties.principalId + roleDefinitionIds: { + 'EventGrid Contributor': '1e241071-0855-49ea-94dc-649edcd759de' + } + } +} + + +@description('Storage Role assignments for microservice managed identity') +module storageRoleAssignments 'utility/roleAssignments.bicep' = { + name: 'storageIAM-${serviceName}-${timestamp}' + scope: resourceGroup(storageResourceGroupName) + params: { + principalId: managedIdentity.properties.principalId + roleDefinitionIds: { + Contributor: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + } +} + +@description('API Key for microservice (only created if not using Entra)') +module apiKeySecret 'kvSecret.bicep' = if (!useOidc) { + name: 'apiKey-${serviceName}-${timestamp}' + scope: resourceGroup(opsResourceGroupName) + params: { + kvName: kvName + secretName: 'foundationallm-apis-${serviceName}-apikey' + secretValue: useOidc ? '' : apiKey + tags: tags + } +} + +@description('Client secret for microservice (only created if using Entra)') +module apiClientSecret 'kvSecret.bicep' = if (useOidc) { + name: 'apiClientSecret-${serviceName}-${timestamp}' + scope: resourceGroup(opsResourceGroupName) + params: { + kvName: kvName + secretName: 'foundationallm-apis-${serviceName}-entra-clientsecret' + secretValue: useOidc ? clientSecret : '' + tags: tags + } +} diff --git a/deploy/starter/infra/app/acaService.bicep b/deploy/starter/infra/app/acaService.bicep index f089db2f4f..7c6cdb8fd3 100644 --- a/deploy/starter/infra/app/acaService.bicep +++ b/deploy/starter/infra/app/acaService.bicep @@ -3,6 +3,7 @@ param location string = resourceGroup().location param tags object = {} param appConfigName string +param eventgridName string param identityName string param keyvaultName string param containerRegistryName string @@ -110,6 +111,21 @@ resource secretsAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-0 } } +resource eventgrid 'Microsoft.EventGrid/namespaces@2023-12-15-preview' existing = { + name: eventgridName +} + +resource eventGridContributorRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: eventgrid + name: guid(subscription().id, resourceGroup().id, identity.id, 'eventGridContributorRole') + properties: { + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + principalType: 'ServicePrincipal' + principalId: identity.properties.principalId + } +} + module fetchLatestImage '../modules/fetch-container-image.bicep' = { name: '${name}-fetch-image' params: { diff --git a/deploy/starter/infra/main.bicep b/deploy/starter/infra/main.bicep index 463a5f09d9..9ffc00941d 100644 --- a/deploy/starter/infra/main.bicep +++ b/deploy/starter/infra/main.bicep @@ -373,6 +373,7 @@ module acaServices './app/acaService.bicep' = [ for service in services: { location: location tags: tags appConfigName: appConfig.outputs.name + eventgridName: eventgrid.outputs.name identityName: '${abbrs.managedIdentityUserAssignedIdentities}${service.name}-${resourceToken}' keyvaultName: keyVault.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName