diff --git a/application/single_app/config.py b/application/single_app/config.py index f139bbf3..7eae5ac8 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -137,7 +137,7 @@ CUSTOM_COGNITIVE_SERVICES_URL_VALUE = os.getenv("CUSTOM_COGNITIVE_SERVICES_URL_VALUE", "") CUSTOM_SEARCH_RESOURCE_MANAGER_URL_VALUE = os.getenv("CUSTOM_SEARCH_RESOURCE_MANAGER_URL_VALUE", "") CUSTOM_REDIS_CACHE_INFRASTRUCTURE_URL_VALUE = os.getenv("CUSTOM_REDIS_CACHE_INFRASTRUCTURE_URL_VALUE", "") - +CUSTOM_OIDC_METADATA_URL_VALUE = os.getenv("CUSTOM_OIDC_METADATA_URL_VALUE", "") # Azure AD Configuration CLIENT_ID = os.getenv("CLIENT_ID") @@ -172,12 +172,14 @@ KEY_VAULT_DOMAIN = ".vault.usgovcloudapi.net" elif AZURE_ENVIRONMENT == "custom": + OIDC_METADATA_URL = CUSTOM_OIDC_METADATA_URL_VALUE resource_manager = CUSTOM_RESOURCE_MANAGER_URL_VALUE authority = CUSTOM_IDENTITY_URL_VALUE credential_scopes=[resource_manager + "/.default"] cognitive_services_scope = CUSTOM_COGNITIVE_SERVICES_URL_VALUE search_resource_manager = CUSTOM_SEARCH_RESOURCE_MANAGER_URL_VALUE KEY_VAULT_DOMAIN = os.getenv("KEY_VAULT_DOMAIN", ".vault.azure.net") + video_indexer_endpoint = os.getenv("VIDEO_INDEXER_ENDPOINT", "https://api.videoindexer.ai") else: OIDC_METADATA_URL = f"https://login.microsoftonline.com/{TENANT_ID}/v2.0/.well-known/openid-configuration" resource_manager = "https://management.azure.com" diff --git a/application/single_app/route_backend_users.py b/application/single_app/route_backend_users.py index 0ee7cc13..25301879 100644 --- a/application/single_app/route_backend_users.py +++ b/application/single_app/route_backend_users.py @@ -27,7 +27,7 @@ def api_user_search(): if AZURE_ENVIRONMENT == "usgovernment": user_endpoint = "https://graph.microsoft.us/v1.0/users" elif AZURE_ENVIRONMENT == "custom": - user_endpoint = CUSTOM_GRAPH_URL_VALUE + user_endpoint = f"{CUSTOM_GRAPH_URL_VALUE}/v1.0/users" else: user_endpoint = "https://graph.microsoft.com/v1.0/users" diff --git a/deployers/bicep/main.bicep b/deployers/bicep/main.bicep index 86e0cfaa..6ee0cbe3 100644 --- a/deployers/bicep/main.bicep +++ b/deployers/bicep/main.bicep @@ -5,14 +5,12 @@ targetScope = 'subscription' - Region must align to the target cloud environment''') param location string -@description('''The target Azure Cloud environment. -- Accepted values are: AzureCloud, AzureUSGovernment -- Default is AzureCloud''') @allowed([ - 'AzureCloud' - 'AzureUSGovernment' + 'public' + 'usgovernment' + 'custom' ]) -param cloudEnvironment string +param cloudEnvironment string = az.environment().name == 'AzureCloud' ? 'public' : (az.environment().name == 'AzureUSGovernment' ? 'usgovernment' : 'custom') @description('''The name of the application to be deployed. - Name may only contain letters and numbers @@ -126,13 +124,27 @@ param deploySpeechService bool - Default is false''') param deployVideoIndexerService bool +// --- Custom Azure Environment Parameters (for 'custom' azureEnvironment) --- +@description('Custom blob storage URL suffix, e.g. blob.core.usgovcloudapi.net') +param customBlobStorageSuffix string = 'blob.${az.environment().suffixes.storage}' +@description('Custom Graph API URL, e.g. https://graph.microsoft.us') +param customGraphUrl string = az.environment().graph +@description('Custom Identity URL, e.g. https://login.microsoftonline.us') +param customIdentityUrl string = az.environment().authentication.loginEndpoint +@description('Custom Resource Manager URL, e.g. https://management.usgovcloudapi.net') +param customResourceManagerUrl string = az.environment().resourceManager +@description('Custom Cognitive Services scope ex: https://cognitiveservices.azure.com/.default') +param customCognitiveServicesScope string = 'https://cognitiveservices.azure.com/.default' +@description('Custom search resource URL for token audience, e.g. https://search.azure.us') +param customSearchResourceUrl string = 'https://search.azure.com' + //========================================================= // variable declarations for the main deployment //========================================================= var rgName = '${appName}-${environment}-rg' var requiredTags = { application: appName, environment: environment, 'azd-env-name': azdEnvironmentName } var tags = union(requiredTags, specialTags) -var acrCloudSuffix = cloudEnvironment == 'AzureCloud' ? '.azurecr.io' : '.azurecr.us' +var acrCloudSuffix = az.environment().suffixes.acrLoginServer var acrName = toLower('${appName}${environment}acr') var containerRegistry = '${acrName}${acrCloudSuffix}' var containerImageName = '${containerRegistry}/${imageName}' @@ -369,6 +381,13 @@ module appService 'modules/appService.bicep' = { enterpriseAppClientSecret: enterpriseAppClientSecret authenticationType: authenticationType keyVaultUri: keyVault.outputs.keyVaultUri + // --- Custom Azure Environment Parameters (for 'custom' azureEnvironment) --- + customBlobStorageSuffix: customBlobStorageSuffix + customGraphUrl: customGraphUrl + customIdentityUrl: customIdentityUrl + customResourceManagerUrl: customResourceManagerUrl + customCognitiveServicesScope: customCognitiveServicesScope + customSearchResourceUrl: customSearchResourceUrl } } @@ -462,7 +481,6 @@ module setPermissions 'modules/setPermissions.bicep' = if (configureApplicationP name: 'setPermissions' scope: rg params: { - webAppName: appService.outputs.name authenticationType: authenticationType enterpriseAppServicePrincipalId: enterpriseAppServicePrincipalId @@ -487,11 +505,9 @@ module setPermissions 'modules/setPermissions.bicep' = if (configureApplicationP //========================================================= // output required for both predeploy and postprovision scripts in azure.yaml output var_rgName string = rgName - -// output values required for predeploy script in azure.yaml output var_webService string = appService.outputs.name output var_imageName string = contains(imageName, ':') ? split(imageName, ':')[0] : imageName -output var_imageTag string = split(imageName, ':')[1] +output var_imageTag string = contains(imageName, ':') ? split(imageName, ':')[1] : 'latest' output var_containerRegistry string = containerRegistry output var_acrName string = toLower('${appName}${environment}acr') diff --git a/deployers/bicep/modules/appService.bicep b/deployers/bicep/modules/appService.bicep index 4f70f03a..e9f44d96 100644 --- a/deployers/bicep/modules/appService.bicep +++ b/deployers/bicep/modules/appService.bicep @@ -25,6 +25,25 @@ param authenticationType string param enterpriseAppClientSecret string = '' param keyVaultUri string +// --- Custom Azure Environment Parameters (for 'custom' azureEnvironment) --- +@description('Custom blob storage URL suffix, e.g. blob.core.usgovcloudapi.net') +param customBlobStorageSuffix string? +@description('Custom Graph API URL, e.g. https://graph.microsoft.us') +param customGraphUrl string? +@description('Custom Identity URL, e.g. https://login.microsoftonline.us') +param customIdentityUrl string? +@description('Custom Resource Manager URL, e.g. https://management.usgovcloudapi.net') +param customResourceManagerUrl string? + +@description('Custom Cognitive Services scope ex: https://cognitiveservices.azure.com/.default') +param customCognitiveServicesScope string? + +@description('Custom search resource URL for token audience, e.g. https://search.azure.us') +param customSearchResourceUrl string? + +var tenantId = tenant().tenantId +var openIdMetadataUrl = '${az.environment().authentication.loginEndpoint}/${tenantId}/v2.0/.well-known/openid-configuration' + // Import diagnostic settings configurations module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) { name: 'diagnosticConfigs' @@ -45,7 +64,6 @@ resource searchService 'Microsoft.Search/searchServices@2025-05-01' existing = { resource openAiService 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = { name: openAiServiceName } - resource documentIntelligence 'Microsoft.CognitiveServices/accounts@2025-06-01' existing = { name: documentIntelligenceServiceName } @@ -53,7 +71,7 @@ resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { name: appInsightsName } -var acrDomain = azurePlatform == 'AzureUSGovernment' ? '.azurecr.us' : '.azurecr.io' +var acrDomain = az.environment().suffixes.acrLoginServer // add web app resource webApp 'Microsoft.Web/sites@2022-03-01' = { @@ -70,16 +88,14 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = { ftpsState: 'Disabled' healthCheckPath: '/external/healthcheck' appSettings: [ - { name: 'AZURE_ENDPOINT', value: azurePlatform == 'AzureUSGovernment' ? 'usgovernment' : 'public' } - { name: 'SCM_DO_BUILD_DURING_DEPLOYMENT', value: 'false' } - { name: 'AZURE_COSMOS_ENDPOINT', value: cosmosDb.properties.documentEndpoint } + {name: 'AZURE_ENVIRONMENT', value: azurePlatform } + {name: 'SCM_DO_BUILD_DURING_DEPLOYMENT', value: 'false'} + {name: 'AZURE_COSMOS_ENDPOINT', value: cosmosDb.properties.documentEndpoint} { name: 'AZURE_COSMOS_AUTHENTICATION_TYPE', value: toLower(authenticationType) } - // Only add this setting if authenticationType is 'key' ...(authenticationType == 'key' ? [{ name: 'AZURE_COSMOS_KEY', value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/cosmos-db-key)' }] : []) - { name: 'TENANT_ID', value: tenant().tenantId } { name: 'CLIENT_ID', value: enterpriseAppClientId } { @@ -145,6 +161,16 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = { { name: 'XDT_MicrosoftApplicationInsights_BaseExtensions', value: 'disabled' } { name: 'XDT_MicrosoftApplicationInsights_Mode', value: 'recommended' } { name: 'XDT_MicrosoftApplicationInsights_PreemptSdk', value: 'disabled' } + ...(azurePlatform == 'custom' ? [ + {name: 'CUSTOM_GRAPH_URL_VALUE', value: customGraphUrl} + {name: 'CUSTOM_IDENTITY_URL_VALUE', value: customIdentityUrl} + {name: 'CUSTOM_RESOURCE_MANAGER_URL_VALUE', value: customResourceManagerUrl} + {name: 'CUSTOM_BLOB_STORAGE_URL_VALUE', value: customBlobStorageSuffix} + {name: 'CUSTOM_COGNITIVE_SERVICES_URL_VALUE', value: customCognitiveServicesScope} + {name: 'CUSTOM_SEARCH_RESOURCE_MANAGER_URL_VALUE', value: customSearchResourceUrl} + {name: 'KEY_VAULT_DOMAIN', value: az.environment().suffixes.keyvaultDns} + {name: 'CUSTOM_OIDC_METADATA_URL_VALUE', value: openIdMetadataUrl}] + : []) ] } clientAffinityEnabled: false