Skip to main content

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:

RoleCan 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