Azure / Azure/azure-functions-agents-runtime
Add resource-specific managed identity client ID overrides for model providers and sandbox
- Dominant language
- Python
- Stars
- 9
- Forks
- 7
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 20
Description
Add after changes are merged from #23
## Summary
Today the runtime uses `AZURE_CLIENT_ID` as the global managed identity selector for Azure SDK `DefaultAzureCredential` usage. This works well when a single user-assigned managed identity should be used for all Azure resources, but it is too coarse for apps that need separate identities for different runtime features.
We should add resource-specific managed identity client ID settings so users can apply least-privilege access per feature. When a feature-specific client ID setting is present, it should override `AZURE_CLIENT_ID` for that feature only. If the feature-specific setting is absent, the runtime should continue falling back to `AZURE_CLIENT_ID`, then to the default system-assigned/default credential chain behavior.
Session persistence should continue to follow the Azure Functions storage configuration (`AzureWebJobsStorage`, `AzureWebJobsStorage__blobServiceUri`, and `AzureWebJobsStorage__clientId`) rather than introducing a separate runtime-specific identity setting for blob history.
Connector identity selection is intentionally out of scope for this issue because connector integration is expected to move to connector MCPs in a future change. Identity selection for connector MCPs should be tracked with that work.
## Motivation
A Function App may need to access several independent Azure resources:
- Azure OpenAI / Microsoft OpenAI model deployments
- Azure AI Foundry projects/models
- ACA Dynamic Sessions sandbox
These resources may have different RBAC requirements. A single global `AZURE_CLIENT_ID` forces one identity to have all permissions, which is convenient but not ideal for least privilege.
## Proposed behavior
Use this general precedence pattern:
```text
feature-specific client ID
> AZURE_CLIENT_ID
> system-assigned managed identity / default DefaultAzureCredential chain
```
If both the feature-specific setting and `AZURE_CLIENT_ID` are set, the feature-specific setting wins for that feature.
## Features needing identity-specific overrides
### 1. Azure OpenAI / Microsoft OpenAI model provider
**Current behavior:**
Uses `DefaultAzureCredential`, honoring `AZURE_CLIENT_ID`, when `AZURE_OPENAI_API_KEY` is not set.
**Suggested setting:**
```bash
AZURE_OPENAI_CLIENT_ID=
```
**Suggested precedence:**
```text
AZURE_OPENAI_CLIENT_ID
> AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID
> AZURE_CLIENT_ID
> default credential behavior
```
**Notes:**
- Only applies when using Azure OpenAI with managed identity.
- Should not apply when `AZURE_OPENAI_API_KEY` is set, since API key auth bypasses managed identity.
- For Azure OpenAI, the model value is usually the deployment name configured through `AZURE_OPENAI_DEPLOYMENT`.
---
### 2. Azure AI Foundry model provider
**Current behavior:**
Uses `DefaultAzureCredential`, honoring `AZURE_CLIENT_ID`.
**Suggested setting:**
```bash
FOUNDRY_CLIENT_ID=
```
**Suggested precedence:**
```text
FOUNDRY_CLIENT_ID
> AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID
> AZURE_CLIENT_ID
> default credential behavior
```
**Notes:**
- Foundry does not currently use API key auth in this runtime path.
- `AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID` provides a shared model-provider override for users who want the same identity for Azure OpenAI and Foundry.
---
### 3. Generic model-provider identity override
**Suggested setting:**
```bash
AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID=
```
**Purpose:**
A common override for model-provider auth, used by both Azure OpenAI and Foundry unless the provider-specific override is set.
**Suggested precedence:**
For Azure OpenAI:
```text
AZURE_OPENAI_CLIENT_ID
> AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID
> AZURE_CLIENT_ID
> default credential behavior
```
For Foundry:
```text
FOUNDRY_CLIENT_ID
> AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID
> AZURE_CLIENT_ID
> default credential behavior
```
---
### 4. ACA Dynamic Sessions sandbox
**Current behavior:**
Uses `DefaultAzureCredential`, honoring `AZURE_CLIENT_ID`.
**Suggested setting:**
```bash
AZURE_FUNCTIONS_AGENTS_SANDBOX_CLIENT_ID=
```
**Suggested precedence:**
```text
AZURE_FUNCTIONS_AGENTS_SANDBOX_CLIENT_ID
> AZURE_CLIENT_ID
> default credential behavior
```
**Notes:**
- Applies to the token used for ACA Dynamic Sessions / code interpreter calls.
- Allows sandbox execution permissions to be separated from model permissions.
## Explicitly out of scope
### Blob-backed session history
Blob session persistence should continue to use the Azure Functions storage settings:
```text
AzureWebJobsStorage
AzureWebJobsStorage__blobServiceUri
AzureWebJobsStorage__clientId
```
The runtime should not add a separate `AZURE_FUNCTIONS_AGENTS_BLOB_CLIENT_ID` setting unless we later support a runtime-specific storage account that is independent of `AzureWebJobsStorage`.
### Connectors
Connector ARM/data-plane identity selection is out of scope for this issue. Connector integration is expected to move to connector MCPs in a future change. Identity selection for connector MCPs should be tracked as part of that MCP connector work.
## Suggested implementation
Add a small shared helper for building managed identity credentials with override support, for example:
```python
def build_managed_identity_credential(
*client_id_env_names: str,
) -> DefaultAzureCredential:
for name in client_id_env_names:
client_id = os.environ.get(name)
if client_id and client_id.strip():
return DefaultAzureCredential(
managed_identity_client_id=client_id.strip()
)
global_client_id = os.environ.get("AZURE_CLIENT_ID")
if global_client_id and global_client_id.strip():
return DefaultAzureCredential(
managed_identity_client_id=global_client_id.strip()
)
return DefaultAzureCredential()
```
Example usage:
```python
# Azure OpenAI
build_managed_identity_credential(
"AZURE_OPENAI_CLIENT_ID",
"AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID",
)
# Foundry
build_managed_identity_credential(
"FOUNDRY_CLIENT_ID",
"AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID",
)
# Sandbox
build_managed_identity_credential(
"AZURE_FUNCTIONS_AGENTS_SANDBOX_CLIENT_ID",
)
```
The helper should always treat the listed feature-specific env vars as higher priority than `AZURE_CLIENT_ID`.
## Documentation updates
Update README and configuration docs to explain:
- `AZURE_CLIENT_ID` remains the global default identity selector.
- Feature-specific client ID settings override `AZURE_CLIENT_ID` for that feature only.
- If neither feature-specific setting nor `AZURE_CLIENT_ID` is set, the runtime uses normal `DefaultAzureCredential` behavior.
- Blob session history follows the Azure Functions storage identity settings, especially `AzureWebJobsStorage__clientId`.
- Connector identity selection will be handled separately as part of connector MCP support.
Example docs table:
| Feature | Specific setting | Fallback |
|---|---|---|
| Azure OpenAI model provider | `AZURE_OPENAI_CLIENT_ID` or `AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID` | `AZURE_CLIENT_ID` |
| Foundry model provider | `FOUNDRY_CLIENT_ID` or `AZURE_FUNCTIONS_AGENTS_MODEL_CLIENT_ID` | `AZURE_CLIENT_ID` |
| ACA Dynamic Sessions sandbox | `AZURE_FUNCTIONS_AGENTS_SANDBOX_CLIENT_ID` | `AZURE_CLIENT_ID` |
| Blob session history | `AzureWebJobsStorage__clientId` | Follows Azure Functions storage identity config |
## Testing
Add tests to verify:
- Feature-specific setting is used when present.
- Feature-specific setting overrides `AZURE_CLIENT_ID` when both are set.
- `AZURE_CLIENT_ID` is used when the feature-specific setting is absent.
- Bare `DefaultAzureCredential()` is used when no client ID setting is present.
- Azure OpenAI API key auth still bypasses managed identity credential construction.
- Blob session history continues to honor `AzureWebJobsStorage__clientId` and does not use model/sandbox-specific identity settings.
Contributor guide
Research direction
Start by locating managed-identity credential construction for the Azure OpenAI, Foundry, and ACA Dynamic Sessions entry points, then inspect the existing storage identity handling. Add focused tests for each precedence path, API-key bypass, and unchanged blob configuration, and update the README and configuration docs. Done means provider-specific settings override the shared model or global setting while bare DefaultAzureCredential behavior remains the fallback.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, python
- Domain
- authentication, backend, cloud
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100