Reference Architectures
Production-ready reference architectures with security controls annotated at every layer. Use these as starting points for your own designs — adapt them to your specific threat model.
How to Use These Architectures
Architecture Selection Matrix
Choose your starting architecture based on team maturity, regulatory requirements, and deployment model. Most organizations evolve through these patterns as they grow.
| Factor | Three-Tier | Hub-Spoke | Zero Trust | Service Mesh | Secure CI/CD | Hybrid |
|---|---|---|---|---|---|---|
| Team Size | 2–10 | 10+ | Any | 10+ | Any | 20+ |
| Cloud Maturity | Beginner | Intermediate | Intermediate | Advanced | Any | Advanced |
| Regulatory Need | Low | Medium | Medium–High | Low–Med | Any | High |
| Complexity | Low | Medium | Medium | High | Low–Med | High |
| Start Here If… | First cloud app | Multi-team org | Remote-first / BYO device | Microservices already built | Any new pipeline | On-prem + cloud migration |
1. Secure Three-Tier Web Application
The classic architecture for web applications — presentation, application logic, and data layers with security controls at each tier and trust boundary.
Secure Three-Tier Architecture
Key Security Controls
Network Controls
- • Security Groups: least-privilege port access
- • NSG rules: subnet-level deny rules
- • No direct internet access to app/data tier
- • NSG Flow Logs enabled
Application Controls
- • JWT validation at API gateway
- • RBAC/ABAC at service level
- • Input validation + output encoding
- • Rate limiting per authenticated user
Terraform — VNet with Network Security Groups
# Secure VNet with isolated subnets per tier
resource "azurerm_virtual_network" "main" {
name = "secure-app-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
# Public subnet (Application Gateway only - no application code here)
resource "azurerm_subnet" "public" {
name = "public-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.1.0/24"]
}
# Private subnet for application tier
resource "azurerm_subnet" "app" {
name = "app-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.10.0/24"]
}
# Isolated subnet for database (no internet route)
resource "azurerm_subnet" "data" {
name = "data-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.20.0/24"]
}
# NSG: Application Gateway - only HTTPS from internet
resource "azurerm_network_security_group" "appgw" {
name = "appgw-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "appgw_https_in" {
name = "allow-https-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.appgw.name
}
# NSG: App tier - only from Application Gateway subnet
resource "azurerm_network_security_group" "app" {
name = "app-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "app_from_appgw" {
name = "allow-appgw-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8080"
source_address_prefix = "10.0.1.0/24"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.app.name
}
# NSG: Database tier - only from App subnet on port 1433
resource "azurerm_network_security_group" "db" {
name = "db-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "db_from_app" {
name = "allow-app-to-db"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "1433"
source_address_prefix = "10.0.10.0/24"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.db.name
}
resource "azurerm_network_security_rule" "db_deny_outbound" {
name = "deny-all-outbound"
priority = 4096
direction = "Outbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.db.name
}# Secure VNet with isolated subnets per tier
resource "azurerm_virtual_network" "main" {
name = "secure-app-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
# Public subnet (Application Gateway only - no application code here)
resource "azurerm_subnet" "public" {
name = "public-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.1.0/24"]
}
# Private subnet for application tier
resource "azurerm_subnet" "app" {
name = "app-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.10.0/24"]
}
# Isolated subnet for database (no internet route)
resource "azurerm_subnet" "data" {
name = "data-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.20.0/24"]
}
# NSG: Application Gateway - only HTTPS from internet
resource "azurerm_network_security_group" "appgw" {
name = "appgw-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "appgw_https_in" {
name = "allow-https-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.appgw.name
}
# NSG: App tier - only from Application Gateway subnet
resource "azurerm_network_security_group" "app" {
name = "app-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "app_from_appgw" {
name = "allow-appgw-inbound"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8080"
source_address_prefix = "10.0.1.0/24"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.app.name
}
# NSG: Database tier - only from App subnet on port 1433
resource "azurerm_network_security_group" "db" {
name = "db-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "db_from_app" {
name = "allow-app-to-db"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "1433"
source_address_prefix = "10.0.10.0/24"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.db.name
}
resource "azurerm_network_security_rule" "db_deny_outbound" {
name = "deny-all-outbound"
priority = 4096
direction = "Outbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.main.name
network_security_group_name = azurerm_network_security_group.db.name
}Common Misconfigurations
Database subnet has route to internet gateway
Data tier should have NO internet access. Use Private Endpoints for Azure services.
Security groups use 0.0.0.0/0 for app tier ingress
App tier should only accept traffic from the load balancer security group.
Azure SQL instance publicly accessible
Disable public network access. Access DB only through app tier or Azure Bastion.
Monitoring & Observability
# NSG Flow Logs — capture all network traffic for forensics
resource "azurerm_network_watcher_flow_log" "nsg" {
network_watcher_name = azurerm_network_watcher.main.name
resource_group_name = azurerm_resource_group.main.name
name = "nsg-flow-log"
network_security_group_id = azurerm_network_security_group.app.id
storage_account_id = azurerm_storage_account.logs.id
enabled = true
version = 2
retention_policy {
enabled = true
days = 90
}
traffic_analytics {
enabled = true
workspace_id = azurerm_log_analytics_workspace.main.workspace_id
workspace_region = azurerm_log_analytics_workspace.main.location
workspace_resource_id = azurerm_log_analytics_workspace.main.id
interval_in_minutes = 10
}
}
# Microsoft Defender for Cloud — automated threat detection
resource "azurerm_security_center_subscription_pricing" "vms" {
tier = "Standard"
resource_type = "VirtualMachines"
}
resource "azurerm_security_center_subscription_pricing" "sql" {
tier = "Standard"
resource_type = "SqlServers"
}
# Azure Monitor alert — alert on high 5xx error rate
resource "azurerm_monitor_metric_alert" "high_5xx" {
name = "high-5xx-rate"
resource_group_name = azurerm_resource_group.main.name
scopes = [azurerm_application_gateway.main.id]
severity = 1
frequency = "PT5M"
window_size = "PT15M"
criteria {
metric_namespace = "Microsoft.Network/applicationGateways"
metric_name = "UnhealthyHostCount"
aggregation = "Average"
operator = "GreaterThan"
threshold = 0
}
action {
action_group_id = azurerm_monitor_action_group.security_alerts.id
}
}
# Azure Monitor alert — detect unusual sign-in activity
resource "azurerm_monitor_activity_log_alert" "unusual_api" {
name = "unusual-api-calls"
resource_group_name = azurerm_resource_group.main.name
scopes = ["/subscriptions/${data.azurerm_subscription.current.subscription_id}"]
criteria {
category = "Administrative"
level = "Error"
}
action {
action_group_id = azurerm_monitor_action_group.security_alerts.id
}
}# NSG Flow Logs — capture all network traffic for forensics
resource "azurerm_network_watcher_flow_log" "nsg" {
network_watcher_name = azurerm_network_watcher.main.name
resource_group_name = azurerm_resource_group.main.name
name = "nsg-flow-log"
network_security_group_id = azurerm_network_security_group.app.id
storage_account_id = azurerm_storage_account.logs.id
enabled = true
version = 2
retention_policy {
enabled = true
days = 90
}
traffic_analytics {
enabled = true
workspace_id = azurerm_log_analytics_workspace.main.workspace_id
workspace_region = azurerm_log_analytics_workspace.main.location
workspace_resource_id = azurerm_log_analytics_workspace.main.id
interval_in_minutes = 10
}
}
# Microsoft Defender for Cloud — automated threat detection
resource "azurerm_security_center_subscription_pricing" "vms" {
tier = "Standard"
resource_type = "VirtualMachines"
}
resource "azurerm_security_center_subscription_pricing" "sql" {
tier = "Standard"
resource_type = "SqlServers"
}
# Azure Monitor alert — alert on high 5xx error rate
resource "azurerm_monitor_metric_alert" "high_5xx" {
name = "high-5xx-rate"
resource_group_name = azurerm_resource_group.main.name
scopes = [azurerm_application_gateway.main.id]
severity = 1
frequency = "PT5M"
window_size = "PT15M"
criteria {
metric_namespace = "Microsoft.Network/applicationGateways"
metric_name = "UnhealthyHostCount"
aggregation = "Average"
operator = "GreaterThan"
threshold = 0
}
action {
action_group_id = azurerm_monitor_action_group.security_alerts.id
}
}
# Azure Monitor alert — detect unusual sign-in activity
resource "azurerm_monitor_activity_log_alert" "unusual_api" {
name = "unusual-api-calls"
resource_group_name = azurerm_resource_group.main.name
scopes = ["/subscriptions/${data.azurerm_subscription.current.subscription_id}"]
criteria {
category = "Administrative"
level = "Error"
}
action {
action_group_id = azurerm_monitor_action_group.security_alerts.id
}
}2. Secure Cloud Landing Zone (Hub-Spoke)
A multi-account / multi-subscription architecture for enterprise cloud deployments. Central shared services hub with isolated spoke environments for each workload.
Hub-Spoke Cloud Landing Zone
Landing Zone Security Controls
Subscription-Level Controls
- • Azure Policy preventing disabling Defender/Activity Log
- • Mandatory encryption on Blob Storage/Managed Disks/Azure SQL
- • Deny public Blob containers at subscription level
- • Require MFA for all Entra ID users
Network Controls
- • All egress through centralized inspection
- • DNS filtering via Azure DNS Private Resolver
- • NSG Flow Logs to central SIEM
- • No direct internet access in spokes
Azure Policy — Deny Dangerous Actions
{
"properties": {
"displayName": "Deny Dangerous Actions",
"policyType": "Custom",
"mode": "All",
"parameters": {},
"policyRule": {
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Security/securityContacts"
},
{
"field": "Microsoft.Security/securityContacts/alertNotifications",
"equals": "Off"
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
"equals": true
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"not": {
"field": "Microsoft.Storage/storageAccounts/encryption.services.blob.enabled",
"equals": true
}
}
]
}
]
},
"then": {
"effect": "Deny"
}
}
}
}{
"properties": {
"displayName": "Deny Dangerous Actions",
"policyType": "Custom",
"mode": "All",
"parameters": {},
"policyRule": {
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Security/securityContacts"
},
{
"field": "Microsoft.Security/securityContacts/alertNotifications",
"equals": "Off"
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
"equals": true
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"not": {
"field": "Microsoft.Storage/storageAccounts/encryption.services.blob.enabled",
"equals": true
}
}
]
}
]
},
"then": {
"effect": "Deny"
}
}
}
}3. Zero Trust Reference Architecture
Based on NIST SP 800-207. Every access request is authenticated, authorized, and encrypted — regardless of network location.
NIST 800-207 Zero Trust Architecture
Cloudflare Access — Zero Trust App Protection
# Cloudflare Access application (ZTNA - replaces VPN)
resource "cloudflare_access_application" "internal_app" {
zone_id = var.zone_id
name = "Internal Dashboard"
domain = "dashboard.internal.example.com"
session_duration = "1h"
# Require device posture check
auto_redirect_to_identity = true
}
# Access policy: require corporate identity + device posture
resource "cloudflare_access_policy" "corp_policy" {
application_id = cloudflare_access_application.internal_app.id
zone_id = var.zone_id
name = "Corporate Access"
precedence = 1
decision = "allow"
include {
login_method = ["oidc"] # SSO via Okta/Entra
}
require {
device_posture = [
cloudflare_device_posture_rule.disk_encryption.id,
cloudflare_device_posture_rule.os_version.id,
cloudflare_device_posture_rule.firewall_enabled.id
]
}
}
# Device posture: require disk encryption
resource "cloudflare_device_posture_rule" "disk_encryption" {
account_id = var.account_id
type = "disk_encryption"
name = "Require disk encryption"
input { require_all = true }
}# Cloudflare Access application (ZTNA - replaces VPN)
resource "cloudflare_access_application" "internal_app" {
zone_id = var.zone_id
name = "Internal Dashboard"
domain = "dashboard.internal.example.com"
session_duration = "1h"
# Require device posture check
auto_redirect_to_identity = true
}
# Access policy: require corporate identity + device posture
resource "cloudflare_access_policy" "corp_policy" {
application_id = cloudflare_access_application.internal_app.id
zone_id = var.zone_id
name = "Corporate Access"
precedence = 1
decision = "allow"
include {
login_method = ["oidc"] # SSO via Okta/Entra
}
require {
device_posture = [
cloudflare_device_posture_rule.disk_encryption.id,
cloudflare_device_posture_rule.os_version.id,
cloudflare_device_posture_rule.firewall_enabled.id
]
}
}
# Device posture: require disk encryption
resource "cloudflare_device_posture_rule" "disk_encryption" {
account_id = var.account_id
type = "disk_encryption"
name = "Require disk encryption"
input { require_all = true }
}4. Microservices with Service Mesh
Kubernetes-native architecture with Istio service mesh providing automatic mTLS, authorization policies, and observability for all service-to-service communication.
Microservices with Service Mesh (Istio)
Istio Authorization Policy
# Enforce mTLS for all traffic in the mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT # All traffic must be mTLS
---
# Only allow order-svc to call payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-svc-policy
namespace: production
spec:
selector:
matchLabels:
app: payment-svc
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/order-svc"]
to:
- operation:
methods: ["POST"]
paths: ["/api/v1/charges", "/api/v1/refunds"]
---
# Deny all other traffic to payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-svc-deny-all
namespace: production
spec:
selector:
matchLabels:
app: payment-svc
action: DENY
rules: [] # Empty = deny everything not explicitly allowed# Enforce mTLS for all traffic in the mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT # All traffic must be mTLS
---
# Only allow order-svc to call payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-svc-policy
namespace: production
spec:
selector:
matchLabels:
app: payment-svc
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/order-svc"]
to:
- operation:
methods: ["POST"]
paths: ["/api/v1/charges", "/api/v1/refunds"]
---
# Deny all other traffic to payment-svc
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: payment-svc-deny-all
namespace: production
spec:
selector:
matchLabels:
app: payment-svc
action: DENY
rules: [] # Empty = deny everything not explicitly allowedKubernetes Network Policy
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow order-svc to reach payment-svc on port 8080 only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-order-to-payment
namespace: production
spec:
podSelector:
matchLabels:
app: payment-svc
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: order-svc
ports:
- protocol: TCP
port: 8080# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow order-svc to reach payment-svc on port 8080 only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-order-to-payment
namespace: production
spec:
podSelector:
matchLabels:
app: payment-svc
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: order-svc
ports:
- protocol: TCP
port: 80805. Secure CI/CD Pipeline
Security gates at every stage of the pipeline — from commit to production. Every artifact is scanned, signed, and verified before deployment.
Secure CI/CD Pipeline — Security Gates
GitHub Actions — Secure Pipeline
name: Secure CI/CD Pipeline
on: [push, pull_request]
permissions:
contents: read
security-events: write
jobs:
gate-1-secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
gate-2-sast:
needs: gate-1-secrets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/secrets
- name: Dependency scan
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
severity: CRITICAL,HIGH
exit-code: 1 # Fail on critical/high
gate-3-container:
needs: gate-2-sast
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: app:${{ github.sha }}
format: spdx-json
- name: Sign image with Cosign
uses: sigstore/cosign-installer@v3
- run: cosign sign --yes app:${{ github.sha }}
gate-4-deploy:
needs: gate-3-container
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production # Requires approval
steps:
- name: Verify image signature
run: cosign verify app:${{ github.sha }}
- name: Deploy to production
run: kubectl set image deployment/app app=app:${{ github.sha }}name: Secure CI/CD Pipeline
on: [push, pull_request]
permissions:
contents: read
security-events: write
jobs:
gate-1-secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
gate-2-sast:
needs: gate-1-secrets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/secrets
- name: Dependency scan
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
severity: CRITICAL,HIGH
exit-code: 1 # Fail on critical/high
gate-3-container:
needs: gate-2-sast
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: app:${{ github.sha }}
format: spdx-json
- name: Sign image with Cosign
uses: sigstore/cosign-installer@v3
- run: cosign sign --yes app:${{ github.sha }}
gate-4-deploy:
needs: gate-3-container
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production # Requires approval
steps:
- name: Verify image signature
run: cosign verify app:${{ github.sha }}
- name: Deploy to production
run: kubectl set image deployment/app app=app:${{ github.sha }}6. Hybrid / Multi-Cloud Security
Interconnecting on-premises, AWS, Azure, and GCP environments while maintaining consistent security policies and identity federation.
Hybrid / Multi-Cloud Architecture
Identity Federation
- • Single IdP as source of truth (Entra ID / Okta)
- • SAML/OIDC federation to all cloud providers
- • No local cloud accounts — federated only
- • Consistent MFA policy across all environments
Network Security
- • Encrypted site-to-site VPN or ExpressRoute
- • Consistent DNS security across environments
- • Centralized egress inspection
- • Uniform firewall rules via IaC
Data Sovereignty
- • Data residency requirements per region
- • Encryption keys in jurisdiction-appropriate KMS
- • GDPR / data localization compliance
- • Cross-border transfer agreements
Unified Observability
- • Central SIEM aggregating all logs
- • Consistent alert rules across providers
- • CSPM tools covering all clouds
- • Single pane of glass for incidents
Security Architecture Decision Record (ADR) Template
Document every security architecture decision so future teams understand the context. Use this template for each significant decision.
# ADR-{number}: {Title}
## Status
Proposed | Accepted | Deprecated | Superseded by ADR-{n}
## Context
What is the problem or situation that requires a decision?
What are the security requirements and constraints?
## Decision Drivers
- Compliance requirement (e.g., SOC 2, PCI DSS)
- Threat model finding (reference TM-{n})
- Performance / scalability requirement
- Operational complexity budget
## Considered Options
1. **Option A** — Description, pros, cons
2. **Option B** — Description, pros, cons
3. **Option C** — Description, pros, cons
## Decision
We chose **Option B** because...
## Security Implications
- **Risk accepted**: What residual risk remains?
- **Controls required**: What must be implemented alongside?
- **Monitoring**: What should we watch for?
## Consequences
- Positive: What improves?
- Negative: What trade-offs are we making?
- Related decisions: Links to dependent ADRs# ADR-{number}: {Title}
## Status
Proposed | Accepted | Deprecated | Superseded by ADR-{n}
## Context
What is the problem or situation that requires a decision?
What are the security requirements and constraints?
## Decision Drivers
- Compliance requirement (e.g., SOC 2, PCI DSS)
- Threat model finding (reference TM-{n})
- Performance / scalability requirement
- Operational complexity budget
## Considered Options
1. **Option A** — Description, pros, cons
2. **Option B** — Description, pros, cons
3. **Option C** — Description, pros, cons
## Decision
We chose **Option B** because...
## Security Implications
- **Risk accepted**: What residual risk remains?
- **Controls required**: What must be implemented alongside?
- **Monitoring**: What should we watch for?
## Consequences
- Positive: What improves?
- Negative: What trade-offs are we making?
- Related decisions: Links to dependent ADRsUse These as Starting Points
Framework Alignment
NIST SP 800-207: Zero Trust reference architecture (pattern 3)
CIS Controls v8.1: 3 (Data Protection), 6 (Access Control), 12 (Network), 16 (App Security)
Azure Well-Architected: Security Pillar best practices
Related: Security Frameworks → | Case Studies →
Reference Architecture Labs
Deploy and validate these reference architectures hands-on. Each lab builds production-grade infrastructure with real security controls.