Logo
Cloud SecurityAdvanced28 min read

Zero Trust Architecture in the Cloud: A Practical Guide Across AWS, Azure, and GCP

Implement zero trust across all three major clouds — with real CLI commands, Terraform configs, and architecture patterns you can deploy today.

Zero TrustAWSAzureGCPIAMNetwork SecurityBeyondCorpConditional Access
Last updated: 2026-02-17

Zero Trust Is Not a Firewall Replacement#

Let's kill the biggest misconception first: Zero Trust is not a product. It's not a next-gen firewall, it's not an SSE gateway, and it's definitely not something you "deploy on Tuesday."

Zero Trust is an architectural philosophy: never trust, always verify. Every request — whether from inside or outside your network — must be authenticated, authorized, and continuously validated.

In cloud environments, this matters even more. There is no perimeter. Your "internal network" is a shared VPC that any compromised workload can traverse. Your "trusted users" are service accounts with overprivileged IAM roles.

The Five Pillars of Cloud Zero Trust#

Before diving into provider-specific implementations, understand the five pillars:

PillarWhat It MeansCloud Implementation
**Identity**Every access request tied to a verified identityIAM, MFA, federation, service accounts
**Device**Only trusted/compliant devices get accessMDM integration, device posture checks
**Network**Micro-segmentation, no implicit trustVPC design, security groups, private endpoints
**Application**App-level auth, not network-levelService mesh, API gateways, mTLS
**Data**Classification-based access controlsEncryption, DLP, DSPM

AWS: Building Zero Trust#

Identity: IAM Identity Center + SCPs

Start with centralized identity. AWS IAM Identity Center (formerly SSO) is your control plane:

bash
# Enable IAM Identity Center (requires Organizations)
aws sso-admin create-instance-assignment \
  --instance-arn arn:aws:sso:::instance/ssoins-1234567890 \
  --target-id 123456789012 \
  --target-type AWS_ACCOUNT \
  --permission-set-arn arn:aws:sso:::permissionSet/ssoins-1234/ps-abcdef \
  --principal-type USER \
  --principal-id user-id-here

# Create SCP to deny actions without MFA
cat > deny-without-mfa.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyWithoutMFA",
    "Effect": "Deny",
    "NotAction": ["iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "sts:GetSessionToken"],
    "Resource": "*",
    "Condition": {
      "BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}
    }
  }]
}
EOF

aws organizations create-policy \
  --name "RequireMFA" \
  --type SERVICE_CONTROL_POLICY \
  --content file://deny-without-mfa.json

Network: VPC Design with PrivateLink

Never expose services to the internet. Use VPC endpoints and PrivateLink:

hcl
# Terraform: VPC with no internet gateway for workloads
resource "aws_vpc" "zero_trust" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = { Name = "zero-trust-vpc" }
}

# Private subnets only — no public subnets
resource "aws_subnet" "private" {
  count             = 3
  vpc_id            = aws_vpc.zero_trust.id
  cidr_block        = cidrsubnet("10.0.0.0/16", 8, count.index)
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags = { Name = "private-${count.index}" }
}

# VPC Endpoint for S3 (gateway type — free)
resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.zero_trust.id
  service_name = "com.amazonaws.${var.region}.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = [aws_route_table.private.id]
}

# VPC Endpoint for SSM (interface type — for session manager)
resource "aws_vpc_endpoint" "ssm" {
  vpc_id              = aws_vpc.zero_trust.id
  service_name        = "com.amazonaws.${var.region}.ssm"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpce.id]
  private_dns_enabled = true
}

AWS Verified Access

For application-level zero trust without a VPN:

bash
# Create a Verified Access trust provider (using IAM Identity Center)
aws ec2 create-verified-access-trust-provider \
  --trust-provider-type user \
  --user-trust-provider-type iam-identity-center \
  --policy-reference-name "IdentityCenter"

# Create Verified Access instance
aws ec2 create-verified-access-instance \
  --description "Zero Trust App Access"

# Attach trust provider to instance
aws ec2 attach-verified-access-trust-provider \
  --verified-access-instance-id vai-0123456789 \
  --verified-access-trust-provider-id vatp-0123456789

Detection: GuardDuty + Security Hub

bash
# Enable GuardDuty with all protection plans
aws guardduty create-detector \
  --enable \
  --data-sources '{
    "S3Logs": {"Enable": true},
    "Kubernetes": {"AuditLogs": {"Enable": true}},
    "MalwareProtection": {"ScanEc2InstanceWithFindings": {"EbsVolumes": true}},
    "RuntimeMonitoring": {"Enable": true}
  }'

# Enable Security Hub with CIS benchmark
aws securityhub enable-security-hub \
  --enable-default-standards

Azure: Building Zero Trust#

Identity: Conditional Access + PIM

Azure's zero trust starts with Entra ID (formerly Azure AD) Conditional Access:

powershell
# Create a Conditional Access policy requiring MFA for all cloud apps
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"

$policy = @{
    displayName = "Require MFA for All Users"
    state = "enabled"
    conditions = @{
        users = @{ includeUsers = @("All") }
        applications = @{ includeApplications = @("All") }
        signInRiskLevels = @("medium", "high")
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("mfa")
    }
    sessionControls = @{
        signInFrequency = @{
            value = 4
            type = "hours"
            isEnabled = $true
        }
    }
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy

# Enable PIM for just-in-time admin access
# Activate a role assignment (time-limited)
$params = @{
    action = "selfActivate"
    principalId = "user-object-id"
    roleDefinitionId = "role-id"
    directoryScopeId = "/"
    justification = "Incident response - ticket INC-2026-0142"
    scheduleInfo = @{
        startDateTime = (Get-Date).ToString("o")
        expiration = @{
            type = "afterDuration"
            duration = "PT4H"
        }
    }
}
New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params

Network: NSG/ASG + Private Link

bash
# Create Application Security Group for web tier
az network asg create -g rg-zerotrust -n asg-web-tier

# Create NSG with micro-segmentation rules
az network nsg create -g rg-zerotrust -n nsg-app-tier

# Allow only web tier to reach app tier on port 8443
az network nsg rule create \
  -g rg-zerotrust --nsg-name nsg-app-tier \
  -n AllowWebToApp --priority 100 \
  --source-asgs asg-web-tier \
  --destination-asgs asg-app-tier \
  --destination-port-ranges 8443 \
  --protocol Tcp --access Allow

# Deny everything else
az network nsg rule create \
  -g rg-zerotrust --nsg-name nsg-app-tier \
  -n DenyAll --priority 4096 \
  --source-address-prefixes '*' \
  --destination-address-prefixes '*' \
  --destination-port-ranges '*' \
  --protocol '*' --access Deny

# Create Private Endpoint for Azure SQL
az network private-endpoint create \
  -g rg-zerotrust -n pe-sql-prod \
  --vnet-name vnet-zerotrust --subnet snet-data \
  --private-connection-resource-id "/subscriptions/.../Microsoft.Sql/servers/sql-prod" \
  --group-ids sqlServer \
  --connection-name sql-prod-connection

Detection: Microsoft Sentinel

bash
# Enable Sentinel on Log Analytics workspace
az sentinel onboarding-state create \
  --resource-group rg-security \
  --workspace-name law-sentinel-prod \
  --name default

# Enable Entra ID data connector
az sentinel data-connector connect \
  --resource-group rg-security \
  --workspace-name law-sentinel-prod \
  --data-connector-name AzureActiveDirectory

GCP: Building Zero Trust#

BeyondCorp Enterprise

Google invented the zero trust concept with BeyondCorp. GCP's implementation is the most mature:

bash
# Enable BeyondCorp Enterprise API
gcloud services enable beyondcorp.googleapis.com

# Create an access level based on device policy
gcloud access-context-manager levels create corp-device-trusted \
  --title="Corporate Trusted Devices" \
  --basic-level-spec=level-spec.yaml \
  --policy=POLICY_ID

# level-spec.yaml content:
# conditions:
#   - devicePolicy:
#       requireScreenlock: true
#       osConstraints:
#         - osType: DESKTOP_CHROME_OS
#           requireVerifiedChromeOs: true
#         - osType: DESKTOP_MAC
#           minimumVersion: "14.0"
#     regions:
#       - AE
#       - US

VPC Service Controls

bash
# Create a service perimeter to protect sensitive APIs
gcloud access-context-manager perimeters create prod-perimeter \
  --title="Production Data Perimeter" \
  --resources="projects/123456789" \
  --restricted-services="bigquery.googleapis.com,storage.googleapis.com,secretmanager.googleapis.com" \
  --access-levels="accessPolicies/POLICY/accessLevels/corp-device-trusted" \
  --policy=POLICY_ID

# Organization policy: enforce uniform bucket-level access
gcloud resource-manager org-policies enable-enforce \
  constraints/storage.uniformBucketLevelAccess \
  --organization=ORG_ID

# Organization policy: restrict VM external IPs
gcloud resource-manager org-policies set-policy \
  --organization=ORG_ID policy.yaml
# policy.yaml:
# constraint: constraints/compute.vmExternalIpAccess
# listPolicy:
#   allValues: DENY

Chronicle SIEM Integration

bash
# Forward GCP audit logs to Chronicle
gcloud logging sinks create chronicle-sink \
  "chronicle.googleapis.com/projects/PROJECT/locations/REGION/instances/INSTANCE" \
  --log-filter='logName:"cloudaudit.googleapis.com"' \
  --organization=ORG_ID

Cross-Cloud Comparison#

CapabilityAWSAzureGCP
**Identity Provider**IAM Identity CenterEntra IDCloud Identity
**Conditional Access**Verified AccessConditional AccessBeyondCorp / IAP
**JIT Privileged Access**IAM Roles AnywherePIMPAM (preview)
**Network Micro-seg**Security Groups + PrivateLinkNSG/ASG + Private LinkVPC Firewall Rules + Service Controls
**API Perimeter**VPC EndpointsPrivate EndpointsVPC Service Controls
**Threat Detection**GuardDutyDefender for CloudSCC + Chronicle
**Policy Guardrails**SCPs + Config RulesAzure PolicyOrganization Policies
**MFA Enforcement**IAM Policy ConditionsConditional AccessContext-Aware Access

Zero Trust Authentication Flow#

Common Pitfalls#

1. "We deployed a ZTNA tool so we're zero trust now." A tool is not an architecture. Zero trust requires changes to identity, network, application, and data layers simultaneously.

2. Overprivileged service accounts. You locked down human access but your Terraform service account has AdministratorAccess. Attackers target the weakest link.

3. Ignoring east-west traffic. Most organizations focus on north-south (ingress/egress) but lateral movement between workloads is the real killer. Implement micro-segmentation.

4. No continuous monitoring. Zero trust without detection is just access control. You need GuardDuty/Defender/SCC feeding a SIEM with automated response.

5. Skipping data classification. If you don't know which data is sensitive, you can't apply the right controls. Start with DSPM before building complex policy engines.

Where Elastyx Fits#

Elastyx provides continuous posture validation across all three clouds — the single pane of glass that tells you whether your zero trust architecture is actually working or just configured on paper. We check IAM policies, network segmentation, encryption settings, and detection coverage in real-time across AWS, Azure, and GCP simultaneously.

Elastyx Platform

Skip the manual work. Let Elastyx do this continuously.

Everything in this guide — and 1,400+ more checks — running 24/7 across your entire cloud estate.

See Elastyx in Action