AI Setup¶
This page is for the platform team. It describes how to enable the AI agents for an environment: the model endpoint in Azure AI Foundry, the spoke agent (Azure DevOps repository) and the integration agent (GitHub repository). It is a reference design: your deployment may differ.
What is needed¶
| Part | Needed for |
|---|---|
| Azure AI Foundry account with model deployments | All agents |
| Spoke agent configuration and repository permissions | Spoke agent |
| Integration agent configuration, GitHub App and repository permissions | Integration agent |
The agent loop runs inside the portal backend. Foundry is used as a model endpoint (an OpenAI-compatible endpoint that the portal calls with its managed identity). The agents do not run in a Foundry agent service. This follows the accepted decision to move to an in-process agent loop (Decisions).
Subscription and providers¶
The subscription that hosts Foundry needs these resource providers registered: Microsoft.App, Microsoft.ContainerService, Microsoft.CognitiveServices, Microsoft.KeyVault, Microsoft.Network, Microsoft.Search, Microsoft.Storage. The foundry_platform module also registers providers through an action.
Needs verification
The delivery guidance lists all seven providers and a delegated subnet for AI agents. The Terraform module creates Cosmos DB, AI Search, storage and VNet injection only in bring-your-own mode (see below). In the default mode the search and storage providers and the delegated subnet may not be needed. Confirm with the engineer before you ask the customer to prepare them.
Deploy AI Foundry¶
Add the foundry_platform module to each environment root and pass the endpoint to the backstage module.
module "foundry_platform" {
source = "../../modules/foundry_platform"
location = module.canvas.location
environment = module.canvas.environment
workload_name = module.canvas.workload_short_name
add_data_suffix_to_storage_account = false
ai_user_principal_ids = [
module.backstage.production_slot_identity_principal_id,
]
spoke_config = {
owners_group_object_id = "<owners-group-object-id>"
contributors_group_object_id = "<contributors-group-object-id>"
vnet_id = module.canvas.virtual_network_id
snet_pe_id = module.canvas.subnets.default.id
snet_aif_id = module.canvas.subnets.<agents-subnet>.id
log_analytics_workspace_id = module.canvas.log_analytics_workspace_id
application_insights_name = module.azure_monitor.application_insights_name
application_insights_resource_group_name = module.azure_monitor.application_insights_resource_group_name
}
}
module "backstage" {
# ...
additional_app_settings = {
services__foundryOpenAIEndpoint__https__0 = "${module.foundry_platform.platform.ai_foundry_openai_endpoint}/openai/v1"
}
}
| Input | Meaning |
|---|---|
owners_group_object_id, contributors_group_object_id |
Entra groups that receive the owner and contributor roles on the Foundry account. Most landing zones have these groups already. Ask the Entra administrator to create them if not. |
ai_user_principal_ids |
Identities that may call the models. Pass the portal's production slot identity, which receives the Foundry user role. |
byo_agent_resources_enabled |
Default false: Foundry uses its Microsoft-hosted default agent backend ("Basic"). true deploys Cosmos DB, AI Search and storage plus VNet injection ("Standard"). |
foundry_public_access |
Default false. A private endpoint is always created. |
search_sku |
Default basic, used only with bring-your-own resources |
Resources¶
| Resource | Created |
|---|---|
| AI Services account (S0) and project | Always |
| Model deployments | Always. Defaults are gpt-5.4 and gpt-5.6-terra, standard data zone deployment, capacity 250. |
| Private endpoint, log analytics workspace, role assignments | Always |
| Cosmos DB, AI Search, storage, their private endpoints and role assignments | Only when byo_agent_resources_enabled is true |
Conflicting guidance
The older deployment guide and the module README list Cosmos DB, AI Search, a storage account, a Container App environment and GPT-4o-mini models as always created. The module code does not agree with that. This page follows the code. See Decisions for the status of the related records.
Agent configuration¶
The agents are enabled in the customer's app-config.<customer>.yaml. The agent definitions (instructions, tools, skills) ship with the code and are rendered from templates, so you set only the switches and the repository.
Spoke agent¶
integrations:
azure:
- host: dev.azure.com
useForAI: false # keep false when the agents use the managed identity for repository access
credentials:
- clientId: ${BACKSTAGE_CLIENT_ID}
clientSecret: ${BACKSTAGE_CLIENT_SECRET}
tenantId: ${BACKSTAGE_TENANT_ID}
agents:
spoke:
enabled: true
repository:
organization: '<organisation>'
projectId: '<project>'
repositoryId: '<repository>'
branch: main
paths:
spoke: '/<spokes-folder>/'
spokeSchema: '/<schema-file>.json'
documentation: ''
| Field | Description |
|---|---|
organization, projectId, repositoryId |
Where the spoke configuration repository lives |
branch |
Branch to read from |
paths.spoke |
Folder with the spoke configuration files |
paths.spokeSchema |
JSON schema used for validation |
paths.documentation |
Optional |
additionalSchema, fileNameRules |
Optional extra schema and file naming rules |
Permissions in Azure DevOps (done by a user with organisation-level rights):
- Add the portal's identity to the organisation with access level Basic. Without this step the identity does not appear in the repository security settings.
- On the spoke repository, grant Contribute to that identity.
Without Contribute, branch and pull request calls fail with HTTP 403. The meaning of the spoke files is in Spoke JSON reference and the process in Change workflow.
Integration agent¶
The integration agent works on a GitHub repository and authenticates with a GitHub App.
integrations:
github:
- host: <host>
apiBaseUrl: https://<host>/api/v3
rawBaseUrl: https://<host>/raw
apps:
- appId: ${GITHUB_APP_ID}
installationId: ${GITHUB_APP_INSTALLATION_ID}
webhookSecret: 'webhook' # required by the config, not used
privateKey: ${GITHUB_APP_PRIVATE_KEY}
agents:
integration:
enabled: true
repository:
host: '<host>' # must equal integrations.github[].host
owner: '<organisation>'
repositoryName: '<repository>'
paths:
serviceBus:
schemas: '<schemas-folder>/'
configuration: '<configuration-folder>/'
documentation: ''
An agentsIntegrations.github block with the same shape lets you give the agents access to GitHub without enabling the other GitHub features of the portal (such as TechDocs scanning).
| Setting | Source |
|---|---|
GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID |
Literal app settings in the Terraform root. They are not secret. |
GITHUB_APP_PRIVATE_KEY |
Repository secret passed through app_secrets into Key Vault (Configuration) |
Install the GitHub App on the target organisation, give it access to the integration repository and grant Contents: read and write and Pull requests: read and write. Without these, branch and pull request operations fail with HTTP 403 or 404. See Integration platform for the platform that this agent configures.
Deploy and verify¶
Deploy through the pipeline (Deployment and environments), test or acceptance first.
| Check | Expected |
|---|---|
| Foundry resources | Exist in the expected resource group |
| Role | The portal identity has the Foundry user role on the account |
| Agent configuration | Active in the running portal (the agent shows in the AI Agents page) |
| Repository access | The identity has Contribute on the spoke repository |
| App settings | GITHUB_APP_* and the Foundry endpoint are set on the web app |
| Spoke agent | Test prompt "List all spoke files" works |
| Integration agent | Test prompt "Show me all existing workloads" works |
| MCP | The tool list of the MCP server is not empty |