Azure / Azure/azure-functions-agents-runtime

Add resource-specific managed identity client ID overrides for model providers and sandbox

Open
#24 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.