Security, Governance & Compliance
Turn Agent1 into a trustworthy, auditable, compliant system.
Identity, Access & Compliance
Secure AI agents with enterprise-grade controls.
Deployment is optimized. Agent is configured with safety. Knowledge is grounded in RAG. Now ensure security: Who can access Agent1? How are credentials managed? What happens when something goes wrong? This section covers identity, access control, encryption, and audit trails.
🎯 What This Section Covers
Contoso operates in Europe and handles customer data. Regulations demand:
- ✅ GDPR Article 5: Data must stay in EU (France Central)
- ✅ Data Residency: No keys transmitted outside EU
- ✅ Audit Trails: All Agent1 actions logged and traceable
- ✅ Least Privilege: Only authorized team members can configure Agent1
- ✅ Encryption: In transit and at rest
This section shows how to implement all of these.
🏗️ Security Architecture Overview
🔐 Step 1: Setup Entra ID Groups
Create security groups for role-based access:
# Create Entra ID groups for team roles
az ad group create \
--display-name "Agent1_Developers" \
--mail-nickname "agent1-developers"
az ad group create \
--display-name "Agent1_DevOps" \
--mail-nickname "agent1-devops"
az ad group create \
--display-name "Agent1_Security" \
--mail-nickname "agent1-security"
az ad group create \
--display-name "Agent1_Managers" \
--mail-nickname "agent1-managers"
# Add members to groups
GROUP_ID=$(az ad group show --group "Agent1_Developers" --query id --output tsv)
# Add user to developers group
az ad group member add \
--group "Agent1_Developers" \
--member-id "user-object-id"
🤖 Step 2: Create Managed Identity for Agent1
Agent1 needs its own identity to access Azure services securely:
# Create user-assigned managed identity
az identity create \
--resource-group "agent-rg" \
--name "agent1-identity"
# Get the identity details
IDENTITY_ID=$(az identity show \
--resource-group "agent-rg" \
--name "agent1-identity" \
--query "id" --output tsv)
IDENTITY_PRINCIPAL_ID=$(az identity show \
--resource-group "agent-rg" \
--name "agent1-identity" \
--query "principalId" --output tsv)
echo "Identity Principal ID: $IDENTITY_PRINCIPAL_ID"
🔑 Step 3: Assign RBAC Roles
Grant minimal required permissions:
RESOURCE_GROUP="agent-rg"
SUBSCRIPTION_ID=$(az account show --query "id" --output tsv)
IDENTITY_PRINCIPAL_ID="..." # From previous step
# 1. Agent1 Developers: Can read + modify agent configuration
az role assignment create \
--assignee-object-id "$IDENTITY_PRINCIPAL_ID" \
--role "Cognitive Services OpenAI Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"
# 2. Agent1 DevOps: Can manage deployments
az role assignment create \
--assignee-object-id "$IDENTITY_PRINCIPAL_ID" \
--role "Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"
# 3. Access Cosmos DB
az role assignment create \
--assignee-object-id "$IDENTITY_PRINCIPAL_ID" \
--role "Cosmos DB Built-in Data Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.DocumentDB/databaseAccounts/agent1-cosmos"
# 4. Access Azure AI Search
az role assignment create \
--assignee-object-id "$IDENTITY_PRINCIPAL_ID" \
--role "Search Index Data Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Search/searchServices/agent1-search"
# 5. Access Key Vault (for secrets)
az role assignment create \
--assignee-object-id "$IDENTITY_PRINCIPAL_ID" \
--role "Key Vault Secrets User" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.KeyVault/vaults/agent1-vault"
🔒 Step 4: Configure Key Vault Access Policies
Store credentials securely:
VAULT_NAME="agent1-vault"
IDENTITY_PRINCIPAL_ID="..."
# Grant Managed Identity access to Key Vault
az keyvault set-policy \
--name "$VAULT_NAME" \
--object-id "$IDENTITY_PRINCIPAL_ID" \
--secret-permissions get list \
--key-permissions get list
# Store secrets
az keyvault secret set \
--vault-name "$VAULT_NAME" \
--name "openai-api-key" \
--value "sk-proj-..." \
--expires $(date -u -d "+1 year" +%s)
az keyvault secret set \
--vault-name "$VAULT_NAME" \
--name "cosmos-connection-string" \
--value "AccountEndpoint=https://...;AccountKey=...;"
# Retrieve secret at runtime
az keyvault secret show \
--vault-name "$VAULT_NAME" \
--name "openai-api-key" \
--query "value" --output tsv
📋 Step 5: Enable Audit Logging
Track all actions for compliance:
from azure.monitor.query import LogsQueryClient, LogsQueryStatus
from azure.identity import DefaultAzureCredential
from datetime import datetime, timedelta
class AuditLogger:
def __init__(self):
self.credential = DefaultAzureCredential()
self.logs_client = LogsQueryClient(credential=self.credential)
self.workspace_id = "/subscriptions/.../resourcegroups/.../providers/microsoft.operationalinsights/workspaces/agent1-logs"
async def log_agent_action(self, action: str, details: dict):
"""Log agent actions for audit trail"""
import json
from datetime import datetime
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"action": action,
"details": details,
"source": "agent1"
}
# In production: Send to Azure Monitor / Log Analytics
print(f"✅ Audit Log: {json.dumps(log_entry)}")
async def query_audit_logs(self, action_filter: str = None, days_back: int = 7) -> list:
"""Query audit logs for compliance"""
kusto_query = f"""
AuditLogs
| where Timestamp >= ago({days_back}d)
| where TargetResources contains "agent1"
| project Timestamp, Identity, Activity, Result
| sort by Timestamp desc
"""
if action_filter:
kusto_query += f"\n| where Activity contains '{action_filter}'"
try:
response = self.logs_client.query_workspace(
workspace_id=self.workspace_id,
query=kusto_query,
timespan=timedelta(days=days_back)
)
if response.status == LogsQueryStatus.SUCCESS:
return response.tables
else:
print(f"Query failed: {response.error}")
return []
except Exception as e:
print(f"Error querying logs: {e}")
return []
# Usage
audit = AuditLogger()
# Log various actions
await audit.log_agent_action("model_deployment", {
"model": "gpt-4o",
"region": "France Central",
"timestamp": "2026-09-28T15:00:00Z"
})
await audit.log_agent_action("safety_violation_detected", {
"input": "How to break into systems",
"severity": "high",
"action": "blocked"
})
await audit.log_agent_action("tool_approval_requested", {
"tool": "process_refund",
"amount": 50,
"requester": "agent1",
"approver": "john@company.com"
})
# Query logs
logs = await audit.query_audit_logs(action_filter="tool_approval")
🔒 Step 6: Encryption In Transit & At Rest
Ensure data is always protected:
# Enable encryption at rest (already default in Azure services)
# 1. Azure OpenAI - Always TLS 1.2+
az cognitiveservices account create \
--name agent1-openai \
--resource-group agent-rg \
--kind OpenAI \
--sku s0 \
--location francecentral \
--https-only true # Force HTTPS only
# 2. Cosmos DB - Encryption at rest with customer-managed keys
az cosmosdb create \
--name agent1-cosmos \
--resource-group agent-rg \
--locations regionName=francecentral failoverPriority=0 \
--default-consistency-level "Strong" \
--enable-automatic-failover false \
--key-uri "https://agent1-vault.vault.azure.net/keys/cosmos-key/..."
# 3. Azure Storage - Default encryption enabled
az storage account create \
--name agent1storage \
--resource-group agent-rg \
--location francecentral \
--sku Standard_LRS \
--kind StorageV2 \
--https-only true \
--min-tls-version TLS1_2
# 4. Verify TLS versions enforced
az storage account update \
--name agent1storage \
--resource-group agent-rg \
--min-tls-version TLS1_2
🛡️ Step 7: Network Security (Firewall & Private Endpoints)
Restrict access to Azure services:
VAULT_NAME="agent1-vault"
COSMOS_NAME="agent1-cosmos"
RESOURCE_GROUP="agent-rg"
# 1. Key Vault: Deny all public traffic, allow only VNet
az keyvault update \
--name "$VAULT_NAME" \
--public-network-access Disabled
# 2. Cosmos DB: Enable firewall
az cosmosdb update \
--name "$COSMOS_NAME" \
--resource-group "$RESOURCE_GROUP" \
--enable-virtual-network true \
--virtual-network-rules "/subscriptions/.../subnets/agent-subnet"
# 3. Create private endpoints for access
az network private-endpoint create \
--name "agent1-vault-pe" \
--resource-group "$RESOURCE_GROUP" \
--vnet-name "agent-vnet" \
--subnet "agent-subnet" \
--private-connection-resource-id "/subscriptions/.../keyVaults/$VAULT_NAME" \
--group-ids "vault" \
--connection-name "agent1-vault-connection"
📊 Complete Security Example
async def secure_agent_setup():
"""Complete setup with all security controls"""
# 1. Initialize with Managed Identity (no keys!)
print("1️⃣ Initializing with Managed Identity...")
credential = DefaultAzureCredential()
# 2. Create clients with secure auth
print("2️⃣ Creating secure clients...")
from azure.ai.openai import AzureOpenAI
from azure.cosmos.aio import CosmosClient
openai_client = AzureOpenAI(
api_version="2024-08-01-preview",
endpoint="https://agent1.openai.azure.com/",
credential=credential # ✅ Managed Identity
)
cosmos_client = CosmosClient(
url="https://agent1.documents.azure.com:443/",
credential=credential # ✅ Managed Identity
)
# 3. Initialize audit logging
print("3️⃣ Setting up audit logging...")
audit = AuditLogger()
# 4. Log setup event
print("4️⃣ Logging setup...")
await audit.log_agent_action("agent_initialization", {
"identity": "agent1-identity",
"region": "francecentral",
"encryption": "enabled",
"audit_logging": "enabled"
})
# 5. Retrieve encrypted secrets from Key Vault
print("5️⃣ Loading encrypted secrets...")
from azure.keyvault.secrets.aio import SecretClient
vault_client = SecretClient(
vault_url="https://agent1-vault.vault.azure.net/",
credential=credential
)
api_key = await vault_client.get_secret("openai-api-key")
# 6. Perform secure operation
print("6️⃣ Running secure agent operation...")
await audit.log_agent_action("agent_query_started", {
"query": "What is warranty?",
"authentication": "managed_identity",
"encryption": "tls1.2"
})
# 7. Query audit logs
print("7️⃣ Querying audit logs...")
logs = await audit.query_audit_logs(days_back=1)
print(f"✅ Audit trail: {len(logs)} events logged")
# Run complete setup
asyncio.run(secure_agent_setup())
✅ Security Checklist
Before deploying Agent1 to production:
- Managed Identity assigned (no API keys in code)
- RBAC roles assigned to team members
- Entra ID groups created for access control
- Key Vault enabled for secret management
- Audit logging enabled and verified
- TLS 1.2+ enforced on all endpoints
- Encryption at rest enabled (default in Azure)
- Firewall rules configured (deny public access)
- Private endpoints created where needed
- Data residency verified (data stays in region)
- Compliance requirements documented and met
# ✅ CORRECT: Using Managed Identity (no credentials in code)
from azure.identity import DefaultAzureCredential
from azure.openai import AzureOpenAI
# When Agent1 runs, Azure automatically provides credentials
credential = DefaultAzureCredential()
client = AzureOpenAI(
api_version="2024-06-01",
endpoint="https://contoso-openai.openai.azure.com/",
credential=credential # Managed Identity - no secrets!
)
# Now Agent1 can call OpenAI (if it has permission)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}]
)
RBAC Roles for Contoso's Teams
Contoso needs different permission levels:
Role permissions:
| Role | Can Deploy? | Can Modify Config? | Can View Logs? | Can Manage Access? |
|---|---|---|---|---|
| Owner (agent-admin) | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Contributor (agent-devops) | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
| Operator (agent-operators) | ❌ No | ⚠️ Limited | ✅ Yes | ❌ No |
| Reader (agent-readers) | ❌ No | ❌ No | ✅ Yes | ❌ No |
🔑 Key Management with Azure Key Vault
Not all secrets can use Managed Identity. Vault stores:
- API keys (third-party services)
- Database connection strings
- Encryption keys
- Certificates
Vault Structure for Contoso
contoso-keyvault (France Central)
├── secrets/
│ ├── db-connection-string (Read: agent-devops)
│ ├── openai-api-key (Legacy - shouldn't exist!)
│ └── third-party-api-key (Stripe, etc.)
├── keys/
│ ├── data-encryption-key (Read: agent1-identity)
│ └── backup-encryption-key (Read: agent-admin)
└── certificates/
├── api-certificate (Contoso.com)
└── client-certificate (Agent1 mTLS)
Access Control:
- agent-admin: Full access
- agent-devops: Read secrets only
- agent1-identity (Managed Identity):
Read data-encryption-key only
- agent-operators: No access
Accessing Secrets from Agent1
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
# Managed Identity authenticates (no credentials needed)
credential = DefaultAzureCredential()
client = SecretClient(
vault_url="https://contoso-keyvault.vault.azure.net/",
credential=credential
)
# Retrieve only what Agent1 needs
encryption_key = client.get_secret("data-encryption-key")
# ✅ Secure: Key retrieved at runtime
# ✅ Auditable: Access logged in Azure Monitor
# ✅ Rotatable: Change without redeploying Agent1
🔒 Encryption: In Transit & At Rest
Encryption In Transit
All communication must use TLS 1.2 or higher.
Encryption At Rest
Data stored in Azure services must be encrypted.
# Cosmos DB: Customer-managed encryption key
cosmos_client = CosmosClient(
url="https://contoso-cosmos.documents.azure.com:443/",
credential=credential
)
# Data in Cosmos is encrypted with key from Key Vault
# Contoso controls the key, Microsoft can't decrypt without it
# Azure Storage: CMK (Customer-Managed Key)
storage_client = BlobServiceClient(
account_url="https://contoso.blob.core.windows.net",
credential=credential
)
# Upload file (auto-encrypted with CMK from Key Vault)
with open("sensitive_data.json", "rb") as data:
storage_client.upload_blob(
name="sensitive_data.json",
data=data
)
# File stored encrypted in France Central
# Contoso controls decryption key
📋 Audit Trails: Know What Agent1 Does
Every action Agent1 takes must be logged.
Activity Log (Azure Portal)
All infrastructure changes logged:
{
"timestamp": "2026-09-28T14:35:22Z",
"operation": "Create_APIDeployment",
"principal": {
"id": "agent-devops@contoso.com",
"type": "Group"
},
"resource": {
"id": "/subscriptions/.../providers/Microsoft.CognitiveServices/accounts/contoso-openai/deployments/gpt-4o-prod",
"type": "OpenAI Deployment"
},
"details": {
"model": "gpt-4o",
"version": "2024-08-06",
"capacity": 20,
"sku": "Standard"
},
"result": "Succeeded"
}
Application Insights: Agent Interactions
Every customer interaction logged:
from azure.monitor.opentelemetry import AzureMonitorTraceExporter
import logging
# Log every agent action
logger = logging.getLogger("agent1")
async def log_agent_action(
customer_id: str,
question: str,
answer: str,
knowledge_sources: list,
safety_score: float
):
"""Log every Agent1 decision for audit trail"""
logger.info(
"agent_query",
extra={
"customer_id": customer_id,
"question": question[:100], # First 100 chars
"answer_length": len(answer),
"knowledge_sources": len(knowledge_sources),
"safety_score": safety_score,
"timestamp": datetime.now().isoformat(),
"user_agent": request.headers.get("User-Agent")
}
)
KQL Queries: Finding Issues
Security team can query logs:
// Find all Agent1 queries with low safety score
customEvents
| where name == "agent_query"
| where todouble(customDimensions.safety_score) < 0.8
| summarize
count(),
avg(todouble(customDimensions.safety_score)) by bin(timestamp, 1h)
| render timechart
🛡️ GDPR & Data Residency
Contoso's compliance requirements enforced at every layer:
🚨 Incident Response
If security event occurs, Contoso can respond quickly.
async def handle_security_incident(incident_type: str, details: dict):
"""Respond to security event"""
# Step 1: Log incident
logger.critical(
"security_incident",
extra={"type": incident_type, "details": details}
)
# Step 2: Notify security team
await notify_slack_channel(
channel="#security-alerts",
message=f"🚨 {incident_type}: {details}"
)
# Step 3: If needed, disable Agent1
if incident_type == "possible_jailbreak":
await disable_agent_endpoint()
logger.critical("Agent1 endpoint disabled for investigation")
# Step 4: Preserve evidence
await export_logs_to_long_term_storage(incident_id)
# Example incidents:
# ✓ Safety score drops below threshold
# ✓ Unauthorized access attempt detected
# ✓ Managed Identity permissions changed unexpectedly
🔍 Security Checklist for Contoso
Before going to production, verify:
- ✅ Identity: Agent1 uses Managed Identity (no keys in code)
- ✅ RBAC: Team members have least-privilege roles
- ✅ Key Vault: All secrets stored securely, rotated regularly
- ✅ Encryption: In-transit (TLS 1.2+) and at-rest (CMK)
- ✅ Audit: All actions logged in Azure Monitor
- ✅ Data Residency: All services in France Central (GDPR)
- ✅ Incident Response: Team trained, runbooks documented
- ✅ Compliance: Annual audit passed
📚 What's Next?
Explore these security and governance topics as detailed guides are added:
- Entra ID & Managed Identity - Identity setup
- RBAC & Role Assignment - Access control
- Key Vault & Secrets Management - Credential management
- Encryption: In Transit & At Rest - Data protection
- Audit Trails & Compliance - Logging and monitoring
These articles will be created as we convert interview questions into independent blog posts.
Last updated: 2026-09-28
Part of: Azure Foundry Learning Path