Zero Trust Security Architecture for Multi-Cloud Environments
The perimeter-based security model is dead. When your workloads span AWS, Azure, and GCP, when your employees work from home networks and coffee shops, when your APIs are called by third-party services you do not control, there is no perimeter to defend. Zero trust is not a product you purchase. It is an architectural philosophy: never trust, always verify, enforce least privilege at every layer, and assume breach as your operating posture.
This guide provides the concrete implementation patterns for zero trust across multi-cloud environments. You will get specific service configurations, policy definitions, and architecture decisions drawn from securing production workloads across regulated industries including healthcare (HIPAA), defense (CMMC/FedRAMP), and financial services (PCI-DSS).
Zero Trust Principles Applied to Multi-Cloud
The five pillars of zero trust translate directly to multi-cloud architecture decisions:
Pillar 1: Identity Is the New Perimeter
Every access request, whether from a human, service, or machine, must be authenticated and authorized based on identity, not network location.
Pillar 2: Least Privilege Access
Grant the minimum permissions required for each identity to perform its function. Remove standing access where possible; prefer just-in-time (JIT) access.
Pillar 3: Microsegmentation
Isolate workloads from each other. A compromised web server should not have network access to your database cluster in a different cloud account.
Pillar 4: Continuous Verification
Authentication is not a one-time event. Continuously evaluate trust based on device posture, behavioral patterns, and risk signals.
Pillar 5: Assume Breach
Design your architecture so that a compromise in one component has minimal blast radius. Encrypt data at rest and in transit. Log everything. Detect and respond rapidly.
Identity Federation Across Clouds
The foundation of multi-cloud zero trust is a unified identity plane. You cannot enforce consistent access policies if each cloud has its own disconnected identity system.
Centralized Identity Provider
Use a single Identity Provider (IdP) as the source of truth. Federate into each cloud provider:
┌──────────────────────┐
│ Identity Provider │
│ (Entra ID / Okta) │
│ │
│ - User identities │
│ - Group memberships │
│ - MFA policies │
│ - Device trust │
│ - Risk scoring │
└───┬──────┬──────┬────┘
│ │ │
▼ ▼ ▼
┌───┐ ┌───┐ ┌───┐
│AWS│ │Azure│ │GCP│
│IAM│ │RBAC│ │IAM│
└───┘ └───┘ └───┘
AWS: SAML 2.0 / OIDC Federation
Configure AWS IAM Identity Center (successor to AWS SSO) with your external IdP:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:saml-provider/EntraID"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
},
"ForAnyValue:StringLike": {
"SAML:groups": "cloud-security-team"
}
}
}
]
}
For workload identity (service-to-service), use IAM Roles for Service Accounts (IRSA) in EKS or IAM Roles Anywhere for on-premises workloads:
# IAM Roles Anywhere: Certificate-based authentication for non-AWS workloads
aws rolesanywhere create-trust-anchor \
--name "corporate-pki" \
--source "sourceType=CERTIFICATE_BUNDLE,sourceData={x509CertificateData=$(cat ca-cert.pem)}"
aws rolesanywhere create-profile \
--name "multi-cloud-workload" \
--role-arns "arn:aws:iam::123456789012:role/MultiCloudWorkloadRole" \
--duration-seconds 3600
Azure: Workload Identity Federation
Azure supports workload identity federation, allowing external identity tokens (from AWS, GCP, GitHub, or any OIDC provider) to exchange for Azure AD tokens without storing secrets:
# Create a federated credential for an AWS workload accessing Azure
az ad app federated-credential create \
--id <azure-app-object-id> \
--parameters '{
"name": "aws-workload-federation",
"issuer": "https://cognito-identity.amazonaws.com",
"subject": "us-east-1:12345678-1234-1234-1234-123456789012",
"audiences": ["api://AzureADTokenExchange"]
}'
This eliminates cross-cloud secret management. The AWS workload authenticates to Azure using its AWS identity, verified through OIDC token exchange. No stored passwords or API keys.
GCP: Workload Identity Federation
GCP's Workload Identity Federation follows the same pattern:
# Create a workload identity pool for AWS workloads
gcloud iam workload-identity-pools create aws-pool \
--location="global" \
--display-name="AWS Workloads"
# Add an AWS provider to the pool
gcloud iam workload-identity-pools providers create-aws aws-provider \
--workload-identity-pool="aws-pool" \
--location="global" \
--account-id="123456789012"
# Grant the federated identity access to GCP resources
gcloud projects add-iam-policy-binding my-gcp-project \
--member="principalSet://iam.googleapis.com/projects/GCP_PROJECT_NUMBER/locations/global/workloadIdentityPools/aws-pool/attribute.aws_role/arn:aws:sts::123456789012:assumed-role/CrossCloudRole" \
--role="roles/storage.objectViewer"
Eliminating Long-Lived Credentials
A core zero trust requirement: no long-lived API keys or service account credentials stored anywhere.
Audit for standing credentials:
# AWS: Find IAM users with access keys older than 90 days
aws iam generate-credential-report
aws iam get-credential-report --output text --query 'Content' | base64 --decode | \
awk -F, '$10 == "true" && $11 != "N/A"' | \
while IFS=, read -r user arn create pwd_enabled pwd_last_used pwd_last_changed \
pwd_next_rotation mfa_active key1_active key1_rotated key1_last_used rest; do
echo "User: $user, Key1 Active: $key1_active, Last Rotated: $key1_rotated"
done
# GCP: Find user-managed service account keys
gcloud iam service-accounts list --format="value(email)" | while read sa; do
keys=$(gcloud iam service-accounts keys list --iam-account="$sa" \
--managed-by=user --format="value(name)")
if [ -n "$keys" ]; then
echo "WARNING: $sa has user-managed keys: $keys"
fi
done
Replace all long-lived credentials with: - IAM roles (AWS) - Managed identities (Azure) - Service account impersonation via Workload Identity (GCP) - Short-lived tokens from workload identity federation for cross-cloud access
Network Microsegmentation
Cloud-Native Microsegmentation
Each cloud provides network isolation primitives. Zero trust requires using them at the most granular level, not just VPC/VNet boundaries.
AWS Security Groups as microsegmentation:
# Terraform: Microsegmented security groups
resource "aws_security_group" "api_service" {
name_prefix = "api-service-"
vpc_id = aws_vpc.main.id
# Only allow traffic from the API gateway
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.api_gateway.id]
description = "API Gateway to API Service"
}
# No default egress - explicitly define allowed outbound
egress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.database_proxy.id]
description = "API Service to Database Proxy"
}
egress {
from_port = 443
to_port = 443
protocol = "tcp"
prefix_list_ids = [aws_vpc_endpoint.s3.prefix_list_id]
description = "API Service to S3 via VPC Endpoint"
}
tags = {
Name = "api-service-sg"
ZeroTrustZone = "application"
}
}
Key principle: remove the default "allow all outbound" egress rule. Every outbound connection must be explicitly permitted. This prevents lateral movement if the service is compromised.
Azure Network Security Groups with Application Security Groups:
# Create Application Security Groups for logical grouping
az network asg create -g rg-prod -n asg-web-servers
az network asg create -g rg-prod -n asg-api-servers
az network asg create -g rg-prod -n asg-databases
# NSG rule: Only web servers can talk to API servers on port 8080
az network nsg rule create \
--resource-group rg-prod \
--nsg-name nsg-api-subnet \
--name allow-web-to-api \
--priority 100 \
--source-asgs asg-web-servers \
--destination-asgs asg-api-servers \
--destination-port-ranges 8080 \
--protocol Tcp \
--access Allow
GCP Firewall Rules with Network Tags:
# Allow only tagged API instances to reach tagged database instances
gcloud compute firewall-rules create allow-api-to-db \
--network=production-vpc \
--action=ALLOW \
--rules=tcp:5432 \
--source-tags=api-server \
--target-tags=database-server \
--priority=1000 \
--description="API servers to PostgreSQL databases"
# Deny all other internal traffic (default-deny baseline)
gcloud compute firewall-rules create deny-internal-default \
--network=production-vpc \
--action=DENY \
--rules=all \
--source-ranges=10.0.0.0/8 \
--priority=65534 \
--description="Default deny for internal traffic"
Service Mesh for Application-Layer Zero Trust
Network-level microsegmentation controls which services can communicate. Service mesh adds application-layer controls: mutual TLS, request-level authorization, and traffic observability.
Istio authorization policy (Kubernetes):
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: api-service-policy
namespace: production
spec:
selector:
matchLabels:
app: api-service
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/api-gateway"
namespaces:
- "production"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/v1/*"]
when:
- key: request.headers[x-request-id]
notValues: [""] # Require request tracing header
- from:
- source:
principals:
- "cluster.local/ns/monitoring/sa/prometheus"
to:
- operation:
methods: ["GET"]
paths: ["/metrics", "/healthz"]
This policy enforces that only the API gateway service account can call the API service endpoints, and only Prometheus can scrape metrics. All other traffic is denied by default.
Policy as Code: Unified Policy Engine
Maintaining consistent security policies across three clouds with three different policy languages is unsustainable at scale. Use a unified policy engine.
Open Policy Agent (OPA) / Rego
OPA provides a single policy language (Rego) that evaluates authorization decisions across all environments:
# policy/multi_cloud_access.rego
package multicloud.access
default allow := false
# Allow access if user has the required role AND MFA is verified AND device is compliant
allow if {
user_has_role
mfa_verified
device_compliant
not ip_blocked
}
user_has_role if {
required_role := data.resource_roles[input.resource]
required_role == input.user.roles[_]
}
mfa_verified if {
input.auth_context.mfa_verified == true
time.now_ns() - input.auth_context.mfa_timestamp_ns < (12 * 60 * 60 * 1000000000) # 12 hours
}
device_compliant if {
input.device.os_version >= data.minimum_os_versions[input.device.os]
input.device.disk_encrypted == true
input.device.firewall_enabled == true
input.device.antivirus_current == true
}
ip_blocked if {
net.cidr_contains(data.blocked_ranges[_], input.source_ip)
}
Deploy OPA as a sidecar or centralized service. Every access request across all three clouds passes through OPA for a consistent authorization decision.
Cloud-Native Policy Tools
| Tool | Cloud | Scope | Use Case |
|---|---|---|---|
| AWS SCP (Service Control Policies) | AWS | Organization-wide | Guardrails across all accounts |
| Azure Policy | Azure | Subscription/Management Group | Compliance enforcement |
| GCP Organization Policies | GCP | Organization/Folder | Constraint enforcement |
| OPA/Gatekeeper | Kubernetes (any cloud) | Cluster admission control | Pod security, resource limits |
| HashiCorp Sentinel | Terraform Cloud | IaC pre-deployment | Prevent insecure infrastructure |
Example: Enforcing Encryption Everywhere
AWS SCP: Deny unencrypted resource creation
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedS3",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": ["aws:kms", "AES256"]
},
"Null": {
"s3:x-amz-server-side-encryption": "false"
}
}
},
{
"Sid": "DenyUnencryptedEBSVolumes",
"Effect": "Deny",
"Action": "ec2:CreateVolume",
"Resource": "*",
"Condition": {
"Bool": {
"ec2:Encrypted": "false"
}
}
}
]
}
Azure Policy: Require encryption on storage accounts
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/encryption.services.blob.enabled",
"notEquals": true
}
]
},
"then": {
"effect": "deny"
}
}
GCP Organization Policy: Enforce CMEK
gcloud resource-manager org-policies set-policy \
--organization=ORG_ID \
policy.yaml
# policy.yaml
constraint: constraints/gcp.restrictNonCmekServices
listPolicy:
deniedValues:
- storage.googleapis.com
- bigquery.googleapis.com
- compute.googleapis.com
Secrets Management Across Clouds
Cross-Cloud Secrets Architecture
┌─────────────────────────────────────────┐
│ HashiCorp Vault (Central) │
│ - Dynamic secrets generation │
│ - Lease-based access (auto-revocation) │
│ - Audit logging for all secret access │
│ - Transit engine for encryption │
└──────┬──────────┬──────────┬────────────┘
│ │ │
▼ ▼ ▼
AWS Secrets Azure Key GCP Secret
Manager Vault Manager
(sync/cache) (sync/cache) (sync/cache)
HashiCorp Vault as the central secrets authority with cloud-native secret stores as local caches provides the best balance of unified management and cloud-native integration.
Vault dynamic database credentials (zero standing access):
# Configure Vault's database secrets engine
vault write database/config/production \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/production" \
allowed_roles="readonly,readwrite" \
username="vault_admin" \
password="initial-password"
# Create a role with a 1-hour TTL
vault write database/roles/readonly \
db_name=production \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \
DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
Every credential request creates a unique database user that automatically expires. If compromised, the blast radius is limited to one hour of access.
Detection and Response
Unified Security Event Pipeline
AWS CloudTrail ─┐
Azure Activity Log ─┤──→ Central SIEM ──→ Detection Rules ──→ Response
GCP Audit Logs ─┘ (Sentinel / (Sigma rules) Automation
Splunk /
VPC Flow Logs ─┐ Elastic)
NSG Flow Logs ─┤
VPC Flow Logs (GCP)┘
Cross-Cloud Detection Rules
Write detection rules in Sigma format for portability across SIEM platforms:
title: Cross-Cloud Privilege Escalation Attempt
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: production
description: Detects attempts to escalate privileges across cloud boundaries
author: Security Team
logsource:
category: cloud
product: aws|azure|gcp
detection:
selection_aws:
eventName:
- CreatePolicyVersion
- AttachUserPolicy
- AttachRolePolicy
- PutRolePolicy
userIdentity.type: AssumedRole
sourceIPAddress|cidr:
- 10.0.0.0/8 # Internal network
selection_azure:
operationName:
- Microsoft.Authorization/roleAssignments/write
- Microsoft.Authorization/roleDefinitions/write
caller|endswith: "@external.com"
selection_gcp:
methodName:
- google.iam.admin.v1.SetIamPolicy
- google.iam.admin.v1.CreateRole
authenticationInfo.principalEmail|endswith: ".iam.gserviceaccount.com"
condition: selection_aws or selection_azure or selection_gcp
level: high
tags:
- attack.privilege_escalation
- attack.t1078
Automated Response Playbooks
When a high-severity detection fires, automate the initial containment:
# AWS Lambda: Automated containment for compromised IAM credentials
import boto3
import json
iam = boto3.client("iam")
sns = boto3.client("sns")
def handler(event, context):
"""Triggered by EventBridge rule matching GuardDuty finding."""
finding = event["detail"]
finding_type = finding["type"]
if "UnauthorizedAccess:IAMUser" in finding_type:
principal = finding["resource"]["accessKeyDetails"]["userName"]
# Step 1: Attach deny-all policy to quarantine the identity
quarantine_policy = {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
}],
}
iam.put_user_policy(
UserName=principal,
PolicyName="SecurityQuarantine",
PolicyDocument=json.dumps(quarantine_policy),
)
# Step 2: Deactivate all access keys
keys = iam.list_access_keys(UserName=principal)["AccessKeyMetadata"]
for key in keys:
iam.update_access_key(
UserName=principal,
AccessKeyId=key["AccessKeyId"],
Status="Inactive",
)
# Step 3: Notify security team
sns.publish(
TopicArn="arn:aws:sns:us-east-1:123456789012:security-alerts",
Subject=f"CONTAINMENT: {principal} quarantined",
Message=json.dumps({
"action": "automated_containment",
"principal": principal,
"finding_type": finding_type,
"finding_id": finding["id"],
"next_steps": "Review CloudTrail logs, assess blast radius, determine root cause",
}),
)
return {"statusCode": 200, "body": "Containment executed"}
Compliance Mapping
Zero trust architecture maps directly to major compliance frameworks:
| Zero Trust Control | FedRAMP | HIPAA | PCI-DSS 4.0 | CMMC L2 |
|---|---|---|---|---|
| Identity federation + MFA | AC-2, IA-2 | 164.312(d) | 8.3, 8.4 | AC.L2-3.1.1 |
| Least privilege | AC-6 | 164.312(a)(1) | 7.1, 7.2 | AC.L2-3.1.5 |
| Microsegmentation | SC-7 | 164.312(e)(1) | 1.2, 1.3 | SC.L2-3.13.1 |
| Encryption at rest | SC-28 | 164.312(a)(2)(iv) | 3.5 | SC.L2-3.13.16 |
| Encryption in transit | SC-8 | 164.312(e)(1) | 4.1 | SC.L2-3.13.8 |
| Continuous monitoring | SI-4, CA-7 | 164.312(b) | 10.4, 11.5 | AU.L2-3.3.1 |
| Incident response | IR-4, IR-5 | 164.308(a)(6) | 12.10 | IR.L2-3.6.1 |
Implementation Roadmap
Phase 1 (Months 1-2): Identity Foundation
- Deploy centralized IdP with SAML/OIDC federation to all clouds
- Enforce MFA for all human access
- Audit and remove long-lived credentials
- Implement workload identity federation for cross-cloud service access
Phase 2 (Months 3-4): Network Controls
- Implement microsegmentation in each cloud
- Remove default-allow egress rules
- Deploy VPC endpoints/Private Link for cloud service access
- Enable VPC/NSG flow logging
Phase 3 (Months 5-6): Policy and Encryption
- Deploy OPA for unified policy evaluation
- Enforce encryption at rest and in transit across all services
- Implement secrets management with dynamic credentials
- Deploy cloud-native policy constraints (SCPs, Azure Policy, GCP Org Policy)
Phase 4 (Months 7-8): Detection and Response
- Centralize audit logs from all three clouds
- Deploy detection rules using Sigma
- Build automated containment playbooks
- Conduct tabletop exercises and red team assessments
Resources for Deeper Learning
Implementing zero trust at scale requires deep expertise in both security principles and cloud-specific services. The Cloud Security course covers AWS, Azure, and GCP security services in depth, including IAM hardening, encryption management, network security, and compliance automation.
For production-ready security tools, the Security Frameworks collection provides Terraform modules for security baseline deployment, OPA policy libraries, and compliance scanning configurations tested against FedRAMP, HIPAA, and PCI-DSS requirements.
Organizations building multi-cloud platforms with stringent compliance requirements should explore the Enterprise Bundles collection, which includes complete zero trust reference architectures with deployment automation across AWS, Azure, and GCP.
Zero trust is not a destination; it is an operational discipline. Start with identity, extend to network, enforce with policy, and verify with continuous monitoring. The organizations that treat security as an ongoing engineering practice rather than a checkbox exercise are the ones that avoid becoming breach statistics.
Continue Learning
Start Your Cloud Career Today
Access 17 free courses covering AWS, Azure, GCP, DevOps, AI/ML, and cloud security — built by a practicing Senior Cloud Architect with enterprise experience.
Get Free Cloud Career Resources