Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.
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
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- @azure Rule - Use Azure Best Practices: When generating code for Azure, running terminal commands for Azure, or performing operations related to Azure, invoke your `azure_development-get_best_practices` tool if available.
3 changes: 2 additions & 1 deletion infra/abbreviations.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"keyVaultVaults": "kv",
"managedIdentityUserAssignedIdentities": "id",
"cognitiveServicesAccounts": "cog",
"portalDashboards": "dash"
"portalDashboards": "dash",
"networkVirtualNetworks": "vnet"
}
46 changes: 28 additions & 18 deletions infra/app/microblog-app.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,12 @@ param containerAppsEnvironmentName string
param applicationInsightsName string
param exists bool = false

// Key Vault parameters (maintained for future use)
// Key Vault parameters
param keyVaultName string
param openAiApiKeySecretName string = 'AZURE-OPENAI-API-KEY'
param openAiEndpointSecretName string = 'AZURE-OPENAI-ENDPOINT'

// Direct credential parameters
@secure()
param azureOpenAIApiKey string = ''
@secure()
param azureOpenAIEndpoint string = ''

@description('Whether the deployment is running on GitHub Actions')
param runningOnGh string = ''

@description('Id of the user or app to assign application roles')
param principalId string = ''
param openAiApiKeySecretName string = 'azure-openai-api-key'
param openAiEndpointSecretName string = 'azure-openai-endpoint'
param openAiDeploymentNameSecretName string = 'azure-openai-deployment-name'
param openAiApiVersionSecretName string = 'azure-openai-api-version'

@secure()
param appDefinition object
Expand Down Expand Up @@ -118,14 +108,26 @@ resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
}
]
secrets: union([
// Direct value secrets
// Key Vault referenced secrets
{
name: 'azure-openai-api-key'
value: !empty(azureOpenAIApiKey) ? azureOpenAIApiKey : 'placeholder-value'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/${openAiApiKeySecretName}'
identity: identity.id
}
{
name: 'azure-openai-endpoint'
value: !empty(azureOpenAIEndpoint) ? azureOpenAIEndpoint : 'https://placeholder-endpoint.openai.azure.com'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/${openAiEndpointSecretName}'
identity: identity.id
}
{
name: 'azure-openai-deployment-name'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/${openAiDeploymentNameSecretName}'
identity: identity.id
}
{
name: 'azure-openai-api-version'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/${openAiApiVersionSecretName}'
identity: identity.id
}
], map(secrets, secret => {
name: secret.secretRef
Expand Down Expand Up @@ -155,6 +157,14 @@ resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
name: 'AZURE_OPENAI_ENDPOINT'
secretRef: 'azure-openai-endpoint'
}
{
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
secretRef: 'azure-openai-deployment-name'
}
{
name: 'AZURE_OPENAI_API_VERSION'
secretRef: 'azure-openai-api-version'
}
],
env,
map(secrets, secret => {
Expand Down
90 changes: 56 additions & 34 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,12 @@ param runningOnGh string = ''

param microblogAppExists bool = false
@secure()
param microblogAppDefinition object = {
settings: []
}
param microblogAppDefinition object

@description('Id of the user ir app to assign application roles')
param principalId string = ''

// Azure OpenAI parameters
@description('Azure OpenAI API Key (leave empty if using Managed Identity)')
@secure()
param azureOpenAIApiKey string

@description('Azure OpenAI Endpoint URL')
param azureOpenAIEndpoint string = ''

Expand Down Expand Up @@ -105,6 +99,24 @@ module keyVault './shared/keyvault.bicep' = {
scope: rg
}

// Virtual Network resource
module vnet './shared/vnet.bicep' = {
name: 'vnet'
params: {
name: '${abbrs.networkVirtualNetworks}${resourceToken}'
location: location
tags: tags
addressPrefix: '10.0.0.0/16'
subnets: [
{
name: 'infrastructure-subnet'
addressPrefix: '10.0.0.0/23'
}
]
}
scope: rg
}

// Container Apps Environment
module appsEnv './shared/apps-env.bicep' = {
name: 'apps-env'
Expand All @@ -114,6 +126,8 @@ module appsEnv './shared/apps-env.bicep' = {
tags: tags
applicationInsightsName: monitoring.outputs.applicationInsightsName
logAnalyticsWorkspaceName: monitoring.outputs.logAnalyticsWorkspaceName
vnetName: vnet.outputs.name
infraSubnetName: 'infrastructure-subnet'
}
scope: rg
}
Expand Down Expand Up @@ -171,13 +185,15 @@ module microblogApp './app/microblog-app.bicep' = {
containerAppsEnvironmentName: appsEnv.outputs.name
applicationInsightsName: monitoring.outputs.applicationInsightsName
keyVaultName: keyVault.outputs.name
azureOpenAIApiKey: azureOpenAIApiKey
azureOpenAIEndpoint: createNewOpenAIResource ? openAi.outputs.endpoint : azureOpenAIEndpoint

// Key Vault secret names
openAiApiKeySecretName: 'azure-openai-api-key'
openAiEndpointSecretName: 'azure-openai-endpoint'
openAiDeploymentNameSecretName: 'azure-openai-deployment-name'
openAiApiVersionSecretName: 'azure-openai-api-version'

// Deployment control parameters
exists: microblogAppExists
principalId: principalId
runningOnGh: runningOnGh

// Application configuration
appDefinition: union(microblogAppDefinition, {
Expand All @@ -191,9 +207,8 @@ module microblogApp './app/microblog-app.bicep' = {
name: 'AZURE_KEY_VAULT_ENDPOINT'
value: keyVault.outputs.endpoint
}
// OpenAI configuration parameters
// Note: These are now referenced via Key Vault in the container
// but we maintain them in appDefinition for orchestration purposes
// OpenAI configuration parameters - included here for orchestration purposes
// These are now set as Key Vault secrets and referenced as container app environment variables
{
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
value: azureOpenAIDeploymentName
Expand All @@ -210,11 +225,7 @@ module microblogApp './app/microblog-app.bicep' = {
})
}
scope: rg
dependsOn: [
// Ensure Key Vault secrets are created before Container App deployment
openAiKeySecret
openAiEndpointSecret
]
dependsOn: [openAiKeySecret, openAiEndpointSecret, openAiDeploymentNameSecret, openAiApiVersionSecret]
}

// Add OpenAI RBAC Roles
Expand Down Expand Up @@ -246,30 +257,41 @@ module openAiKeySecret 'shared/keyvault-secret.bicep' = {
name: 'openai-key-secret'
params: {
keyVaultName: keyVault.outputs.name
secretName: 'AZURE-OPENAI-API-KEY'
secretValue: createNewOpenAIResource
? listKeys(resourceId('Microsoft.CognitiveServices/accounts', '${abbrs.cognitiveServicesAccounts}${resourceToken}'), '2023-05-01').key1
: azureOpenAIApiKey
secretName: 'azure-openai-api-key'
secretValue: createNewOpenAIResource ? openAi.outputs.apiKey : ''
}
scope: rg
dependsOn: [
keyVault
]
}

module openAiEndpointSecret 'shared/keyvault-secret.bicep' = {
name: 'openai-endpoint-secret'
params: {
keyVaultName: keyVault.outputs.name
secretName: 'AZURE-OPENAI-ENDPOINT'
secretValue: createNewOpenAIResource
? openAi.outputs.endpoint
: azureOpenAIEndpoint
secretName: 'azure-openai-endpoint'
secretValue: createNewOpenAIResource ? openAi.outputs.endpoint : azureOpenAIEndpoint
}
scope: rg
}

// Add deployment name and API version as secrets in Key Vault
module openAiDeploymentNameSecret 'shared/keyvault-secret.bicep' = {
name: 'openai-deployment-name-secret'
params: {
keyVaultName: keyVault.outputs.name
secretName: 'azure-openai-deployment-name'
secretValue: azureOpenAIDeploymentName
}
scope: rg
}

module openAiApiVersionSecret 'shared/keyvault-secret.bicep' = {
name: 'openai-api-version-secret'
params: {
keyVaultName: keyVault.outputs.name
secretName: 'azure-openai-api-version'
secretValue: azureOpenAIApiVersion
}
scope: rg
dependsOn: [
keyVault
]
}

// Outputs
Expand All @@ -289,7 +311,7 @@ output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString

// OpenAI outputs
output AZURE_OPENAI_API_KEY string = '@Microsoft.KeyVault(SecretUri=${keyVault.outputs.endpoint}/secrets/AZURE-OPENAI-API-KEY)'
output AZURE_OPENAI_API_KEY string = '@Microsoft.KeyVault(SecretUri=${keyVault.outputs.endpoint}/secrets/azure-openai-api-key)'
output AZURE_OPENAI_ENDPOINT string = createNewOpenAIResource ? openAi.outputs.endpoint : azureOpenAIEndpoint
output AZURE_OPENAI_DEPLOYMENT_NAME string = azureOpenAIDeploymentName
output AZURE_OPENAI_API_VERSION string = azureOpenAIApiVersion
1 change: 1 addition & 0 deletions infra/shared/cognitiveservices.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ output endpoint string = account.properties.endpoint
output id string = account.id
output name string = account.name
output identityPrincipalId string = account.identity.principalId
output apiKey string = listKeys(account.id, account.apiVersion).key1
45 changes: 45 additions & 0 deletions infra/shared/vnet.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@description('Name of the virtual network')
param name string

@description('Azure region where the resource will be deployed')
param location string

@description('Resource tags')
param tags object = {}

@description('Address prefix for the virtual network')
param addressPrefix string = '10.0.0.0/16'

@description('Array of subnet objects with name and addressPrefix')
param subnets array = [
{
name: 'default'
addressPrefix: '10.0.0.0/24'
}
]

resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: name
location: location
tags: tags
properties: {
addressSpace: {
addressPrefixes: [
addressPrefix
]
}
subnets: [for subnet in subnets: {
name: subnet.name
properties: {
addressPrefix: subnet.addressPrefix
delegations: subnet.?delegations ?? []
privateEndpointNetworkPolicies: subnet.?privateEndpointNetworkPolicies ?? 'Enabled'
privateLinkServiceNetworkPolicies: subnet.?privateLinkServiceNetworkPolicies ?? 'Enabled'
serviceEndpoints: subnet.?serviceEndpoints ?? []
}
}]
}
}

output id string = vnet.id
output name string = vnet.name