Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,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")
Expand Down Expand Up @@ -174,12 +174,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")
Comment on lines +177 to +184
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding CUSTOM_OIDC_METADATA_URL_VALUE is good for consistency, but note that the public cloud environment (else block starting at line 185) is missing the 'search_resource_manager' variable definition which is present in both usgovernment (line 173) and custom (line 182) blocks. While this is a pre-existing issue not introduced by this PR, it will cause runtime errors when search_resource_manager is referenced elsewhere in the code for public cloud deployments. Consider adding 'search_resource_manager = "https://search.azure.com"' to the else block in a follow-up change.

Copilot uses AI. Check for mistakes.
else:
OIDC_METADATA_URL = f"https://login.microsoftonline.com/{TENANT_ID}/v2.0/.well-known/openid-configuration"
resource_manager = "https://management.azure.com"
Expand Down
2 changes: 1 addition & 1 deletion application/single_app/route_backend_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
38 changes: 27 additions & 11 deletions deployers/bicep/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions "Expose bicep param to set openai model params for existing OpenAI" but this change is not visible in the provided diffs. Please verify that this change was included in the pull request or update the PR description to accurately reflect the changes made.

Copilot uses AI. Check for mistakes.

@description('''The name of the application to be deployed.
- Name may only contain letters and numbers
Expand Down Expand Up @@ -143,13 +141,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')
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description for this parameter uses "ex:" instead of the more standard "e.g." or "for example". For consistency with other parameter descriptions in the file (which use "e.g."), consider updating this to match.

Suggested change
@description('Custom Cognitive Services scope ex: https://cognitiveservices.azure.com/.default')
@description('Custom Cognitive Services scope e.g. https://cognitiveservices.azure.com/.default')

Copilot uses AI. Check for mistakes.
param customCognitiveServicesScope string = 'https://cognitiveservices.azure.com/.default'
Comment on lines +153 to +154
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for customCognitiveServicesScope is hardcoded to 'https://cognitiveservices.azure.com/.default', which is the public Azure cloud endpoint. This default will be incorrect for users deploying to AzureUSGovernment or other custom clouds, as they use different cognitive services endpoints (e.g., 'https://cognitiveservices.azure.us/.default' for US Government). Consider making this value conditional based on the cloud environment or documenting that users must override this parameter when deploying to non-public clouds.

Suggested change
@description('Custom Cognitive Services scope ex: https://cognitiveservices.azure.com/.default')
param customCognitiveServicesScope string = 'https://cognitiveservices.azure.com/.default'
@description('Custom Cognitive Services scope, e.g. https://cognitiveservices.azure.com/.default (public), https://cognitiveservices.azure.us/.default (US Gov)')
param customCognitiveServicesScope string = az.environment().name == 'AzureUSGovernment' ? 'https://cognitiveservices.azure.us/.default' : 'https://cognitiveservices.azure.com/.default'

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for customCognitiveServicesScope uses the public Azure endpoint ('https://cognitiveservices.azure.com/.default'), but this may be incorrect for government or other custom clouds. When az.environment() detects a non-public cloud but cloudEnvironment is set to 'custom', this will use the wrong cognitive services scope. Consider providing environment-specific defaults based on az.environment().

Suggested change
param customCognitiveServicesScope string = 'https://cognitiveservices.azure.com/.default'
param customCognitiveServicesScope string = az.environment().name == 'AzureUSGovernment' ? 'https://cognitiveservices.azure.us/.default' : 'https://cognitiveservices.azure.com/.default'

Copilot uses AI. Check for mistakes.
@description('Custom search resource URL for token audience, e.g. https://search.azure.us')
param customSearchResourceUrl string = 'https://search.azure.com'
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for customSearchResourceUrl is hardcoded to 'https://search.azure.com', which is the public Azure cloud endpoint. This default will be incorrect for users deploying to AzureUSGovernment or other custom clouds, as they use different search endpoints (e.g., 'https://search.azure.us' for US Government). Consider making this value conditional based on the cloud environment or documenting that users must override this parameter when deploying to non-public clouds.

Suggested change
param customSearchResourceUrl string = 'https://search.azure.com'
param customSearchResourceUrl string = cloudEnvironment == 'usgovernment'
? 'https://search.azure.us'
: (cloudEnvironment == 'public' ? 'https://search.azure.com' : '')

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for customSearchResourceUrl uses the public Azure endpoint ('https://search.azure.com'), but this may be incorrect for government or other custom clouds. When az.environment() detects a non-public cloud but cloudEnvironment is set to 'custom', this will use the wrong search endpoint. Consider using az.environment().suffixes or providing environment-specific defaults.

Suggested change
param customSearchResourceUrl string = 'https://search.azure.com'
param customSearchResourceUrl string = 'https://search.${az.environment().suffixes.search}'

Copilot uses AI. Check for mistakes.

//=========================================================
// 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}'
Expand Down Expand Up @@ -435,6 +447,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

enablePrivateNetworking: enablePrivateNetworking
#disable-next-line BCP318 // expect one value to be null if private networking is disabled
Expand Down Expand Up @@ -540,7 +559,6 @@ module setPermissions 'modules/setPermissions.bicep' = if (configureApplicationP
name: 'setPermissions'
scope: rg
params: {

webAppName: appService.outputs.name
authenticationType: authenticationType
enterpriseAppServicePrincipalId: enterpriseAppServicePrincipalId
Expand Down Expand Up @@ -603,8 +621,6 @@ module privateNetworking 'modules/privateNetworking.bicep' = if (enablePrivateNe
//=========================================================
// output values
//=========================================================


// output values required for postprovision script in azure.yaml
output var_acrName string = toLower('${appName}${environment}acr')
output var_authenticationType string = toLower(authenticationType)
Expand Down Expand Up @@ -637,7 +653,7 @@ output var_videoIndexerName string = deployVideoIndexerService ? videoIndexerSer
// output values required for predeploy script in azure.yaml
output var_containerRegistry string = containerRegistry
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_webService string = appService.outputs.name

// output values required for postup script in azure.yaml
Expand Down
36 changes: 31 additions & 5 deletions deployers/bicep/modules/appService.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ param keyVaultUri string
param enablePrivateNetworking bool
param appServiceSubnetId 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')
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description for this parameter uses "ex:" instead of the more standard "e.g." or "for example". For consistency with other parameter descriptions in the file (which use "e.g."), consider updating this to match.

Suggested change
@description('Custom Cognitive Services scope ex: https://cognitiveservices.azure.com/.default')
@description('Custom Cognitive Services scope, e.g. https://cognitiveservices.azure.com/.default')

Copilot uses AI. Check for mistakes.
param customCognitiveServicesScope string?

@description('Custom search resource URL for token audience, e.g. https://search.azure.us')
param customSearchResourceUrl string?
Comment on lines +32 to +44
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter definitions use nullable types (string?) but main.bicep provides default values for all of these parameters. This creates an inconsistency - either these should be non-nullable string types to match the fact that they always have values from main.bicep's defaults, or the defaults in main.bicep should be removed and null handling should be added when setting the environment variables. The current approach could be confusing for users and may lead to unexpected behavior.

Suggested change
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?
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

Copilot uses AI. Check for mistakes.

var tenantId = tenant().tenantId
var openIdMetadataUrl = '${az.environment().authentication.loginEndpoint}/${tenantId}/v2.0/.well-known/openid-configuration'
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The openIdMetadataUrl variable is computed using az.environment().authentication.loginEndpoint which will always reflect the actual Azure environment being deployed to, not necessarily matching a 'custom' platform setting. If deploying to a truly custom Azure environment (not AzureCloud/AzureUSGovernment), this constructed URL might not be correct. The variable should potentially use customIdentityUrl when azurePlatform is 'custom' to ensure consistency.

Suggested change
var openIdMetadataUrl = '${az.environment().authentication.loginEndpoint}/${tenantId}/v2.0/.well-known/openid-configuration'
var identityBaseUrl = azurePlatform == 'custom' && !empty(customIdentityUrl) ? customIdentityUrl : az.environment().authentication.loginEndpoint
var openIdMetadataUrl = '${identityBaseUrl}/${tenantId}/v2.0/.well-known/openid-configuration'

Copilot uses AI. Check for mistakes.

// Import diagnostic settings configurations
module diagnosticConfigs 'diagnosticSettings.bicep' = if (enableDiagLogging) {
name: 'diagnosticConfigs'
Expand All @@ -47,15 +66,14 @@ 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
}
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' = {
Expand All @@ -77,16 +95,14 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = {
ftpsState: 'Disabled'
healthCheckPath: '/external/healthcheck'
appSettings: [
{ name: 'AZURE_ENDPOINT', value: azurePlatform == 'AzureUSGovernment' ? 'usgovernment' : 'public' }
{ 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 }
{
Expand Down Expand Up @@ -152,6 +168,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}
Comment on lines +172 to +177
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nullable parameters are passed directly to environment variables without null checking. If these parameters are not provided (which is valid for nullable parameters), the environment variables will be set to empty or undefined values. For the 'custom' environment, consider either making these parameters required when azurePlatform is 'custom', or adding validation to ensure they are provided when needed.

Copilot uses AI. Check for mistakes.
{name: 'KEY_VAULT_DOMAIN', value: az.environment().suffixes.keyvaultDns}
{name: 'CUSTOM_OIDC_METADATA_URL_VALUE', value: openIdMetadataUrl}]
: [])
]
}
clientAffinityEnabled: false
Expand Down
Loading