| name | aigbb-azure-security |
| description | Zero-trust security patterns for Azure applications and infrastructure. Cross-language authentication with ChainedTokenCredential (Python, TypeScript, .NET), managed identity, RBAC least-privilege roles, environment variable policies (allowed/forbidden), Azure Key Vault integration, and pre-deployment security checklists. Use when connecting to any Azure service, configuring authentication, setting up RBAC, reviewing environment variables, or validating security posture. Triggers on Azure security, authentication, managed identity, credential, API key, RBAC, role assignment, Key Vault, AZURE_CLIENT_ID, zero trust, ChainedTokenCredential, AzureDeveloperCliCredential, ManagedIdentityCredential, forbidden, allowed environment variables. |
Azure Zero-Trust Security Patterns
Mandatory security patterns for every Azure integration in this repository. No exceptions. No API keys. No compromise.
Community skills: For additional Azure SDK skills, see the microsoft/skills community repository — a collection of reusable Copilot skills maintained by the community.
Core Principle
NEVER use API keys or connection strings for Azure service authentication. All authentication MUST use Microsoft Entra ID (Azure Active Directory) with:
- Azure Developer CLI for local development
- Managed Identity for production deployments
1. Authentication — ChainedTokenCredential
Use ChainedTokenCredential in every application that connects to Azure. This provides seamless local → production authentication without code changes.
Python
from azure.identity import (
AzureDeveloperCliCredential,
ManagedIdentityCredential,
ChainedTokenCredential,
)
def get_azure_credential() -> ChainedTokenCredential:
"""Best-practice credential chain: azd (local) → managed identity (prod)."""
return ChainedTokenCredential(
AzureDeveloperCliCredential(),
ManagedIdentityCredential(),
)
TypeScript
import {
ChainedTokenCredential,
AzureDeveloperCliCredential,
ManagedIdentityCredential,
} from "@azure/identity";
export function getAzureCredential(): ChainedTokenCredential {
return new ChainedTokenCredential(
new AzureDeveloperCliCredential(),
new ManagedIdentityCredential(),
);
}
.NET (C#)
using Azure.Identity;
public static class AzureCredentialHelper
{
public static ChainedTokenCredential GetAzureCredential()
{
return new ChainedTokenCredential(
new AzureDeveloperCliCredential(),
new ManagedIdentityCredential()
);
}
}
2. Service Client Configuration
Always pass credential (never an API key) when constructing Azure SDK clients.
Python Examples
from openai import AzureOpenAI
from azure.identity import get_bearer_token_provider
credential = get_azure_credential()
token_provider = get_bearer_token_provider(
credential, "https://cognitiveservices.azure.com/.default"
)
client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
api_version="2024-12-01-preview",
)
from azure.search.documents import SearchClient
client = SearchClient(
endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
index_name="my-index",
credential=credential,
)
from azure.storage.blob import BlobServiceClient
client = BlobServiceClient(
account_url=os.environ["AZURE_STORAGE_ACCOUNT_URL"],
credential=credential,
)
from azure.cosmos import CosmosClient
client = CosmosClient(
url=os.environ["AZURE_COSMOS_ENDPOINT"],
credential=credential,
)
from azure.keyvault.secrets import SecretClient
client = SecretClient(
vault_url=os.environ["AZURE_KEY_VAULT_ENDPOINT"],
credential=credential,
)
TypeScript Examples
import { AzureOpenAI } from "openai";
import { getBearerTokenProvider } from "@azure/identity";
const credential = getAzureCredential();
const tokenProvider = getBearerTokenProvider(
credential,
"https://cognitiveservices.azure.com/.default",
);
const client = new AzureOpenAI({
azureADTokenProvider: tokenProvider,
endpoint: process.env.AZURE_OPENAI_ENDPOINT!,
apiVersion: "2024-12-01-preview",
});
3. Environment Variable Policy
✅ ALLOWED (endpoints and connection strings)
These are safe — they contain no secrets:
| Variable | Purpose |
|---|
AZURE_CLIENT_ID | REQUIRED — managed identity client ID |
AZURE_OPENAI_ENDPOINT | Azure OpenAI service endpoint |
AZURE_SEARCH_ENDPOINT | Azure AI Search endpoint |
AZURE_STORAGE_ACCOUNT_URL | Storage account blob endpoint |
AZURE_COSMOS_ENDPOINT | Cosmos DB account endpoint |
AZURE_KEY_VAULT_ENDPOINT | Key Vault URI |
APPLICATION_INSIGHTS_CONNECTION_STRING | App Insights (no secret) |
❌ FORBIDDEN (API keys and secrets)
NEVER set these as environment variables:
| Variable | Why Forbidden |
|---|
AZURE_OPENAI_API_KEY | Use managed identity instead |
AZURE_AI_SEARCH_KEY | Use managed identity instead |
AZURE_STORAGE_ACCOUNT_KEY | Use managed identity instead |
AZURE_COSMOS_DB_KEY | Use managed identity instead |
Any *_KEY or *_SECRET | Use Key Vault + managed identity |
4. Infrastructure — Managed Identity
User Assigned Managed Identity (required)
// Always create a User Assigned Managed Identity
module managedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = {
name: 'managed-identity'
params: {
name: '${abbrs.managedIdentityUserAssignedIdentities}${environmentName}'
location: location
tags: tags
}
}
Assign to Container Apps
module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'container-app'
params: {
managedIdentities: {
userAssignedResourceIds: [managedIdentity.outputs.resourceId]
}
containers: [
{
name: 'main'
image: containerImage
env: [
{
name: 'AZURE_CLIENT_ID'
value: managedIdentity.outputs.clientId // ← REQUIRED
}
]
}
]
}
}
5. RBAC — Least Privilege Roles
Use specific roles; never Contributor or Owner.
Common Role Assignments
| Service | Role | Role ID |
|---|
| Key Vault | Key Vault Secrets User | 4633458b-17de-408a-b874-0445c86b69e6 |
| Storage | Storage Blob Data Contributor | ba92f5b4-2d11-453d-a403-e96b0029c9fe |
| Azure OpenAI | Cognitive Services User | 5e0bd9bd-7b93-4f28-af87-19fc36ad61bd |
| AI Search | Search Service Contributor | 7ca78c08-252a-4471-8644-bb5ff32d4ba0 |
| AI Search | Search Index Data Contributor | 8ebe5a00-799e-43f5-93ac-243d3dce84a7 |
| Cosmos DB | Cosmos DB Built-in Data Contributor | 00000000-0000-0000-0000-000000000002 |
| Container Registry | AcrPull | 7f951dda-4ed3-4680-a7ca-43fe172d538d |
Bicep Pattern
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(principalId)) {
name: guid(resource.id, principalId, roleDefinitionId)
scope: resource
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions', roleDefinitionId
)
principalId: principalId
principalType: 'ServicePrincipal'
}
}
Guard with if (!empty(principalId)) to avoid failures when no principal is provided.
6. Configuration Management
Python (pydantic-settings)
from pydantic_settings import BaseSettings
class AzureSettings(BaseSettings):
openai_endpoint: str
search_endpoint: str
storage_account_url: str
cosmos_endpoint: str
keyvault_endpoint: str
client_id: str | None = None
class Config:
env_prefix = "AZURE_"
TypeScript
interface AzureConfig {
openaiEndpoint: string;
searchEndpoint: string;
storageAccountUrl: string;
cosmosEndpoint: string;
keyvaultEndpoint: string;
clientId?: string;
}
.NET (IOptions pattern)
public record AzureSettings
{
public string OpenAiEndpoint { get; init; } = string.Empty;
public string SearchEndpoint { get; init; } = string.Empty;
public string StorageAccountUrl { get; init; } = string.Empty;
public string CosmosEndpoint { get; init; } = string.Empty;
public string KeyVaultEndpoint { get; init; } = string.Empty;
public string? ClientId { get; init; }
}
7. Forbidden Patterns
Code
client = OpenAIClient(endpoint=endpoint, api_key="sk-...")
client = BlobServiceClient(conn_str="DefaultEndpointsProtocol=https;AccountKey=...")
Bicep
// ❌ FORBIDDEN — extracting access keys
var storageKey = storageAccount.listKeys().keys[0].value
// ❌ FORBIDDEN — outputting secrets
output storageKey string = storageAccount.listKeys().keys[0].value
// ❌ FORBIDDEN — access-key auth mode
properties: { authenticationMethod: 'accessKey' }
8. Pre-Deployment Security Checklist
Before deploying any code or infrastructure:
Code Review Quick Scan
Search for forbidden patterns:
api_key= secret= password= listKeys() *_KEY *_SECRET
References