Skill v1.0.1
currentAutomated scan100/1007 files
name: configuring-aws-verified-access-for-ztna description: Configure AWS Verified Access to provide VPN-less zero trust network access to internal applications using identity and device posture verification with Cedar policy language. domain: cybersecurity subdomain: zero-trust-architecture tags: [zero-trust, aws, verified-access, ztna, cedar-policy, vpn-less, identity-verification, device-posture, aws-ram] version: "1.0" author: mahipal license: Apache-2.0
Configuring AWS Verified Access for ZTNA
Overview
AWS Verified Access is a Zero Trust Network Access (ZTNA) service that provides secure, VPN-less access to corporate applications hosted in AWS. It evaluates each access request in real-time against granular conditional access policies written in the Cedar policy language, ensuring access is granted per-application only when specific security requirements such as user identity and device security posture are met and maintained. Verified Access integrates with AWS IAM Identity Center, third-party identity providers (Okta, CrowdStrike, JumpCloud, Jamf), and device management solutions. For multi-account deployments, AWS Resource Access Manager (RAM) enables sharing Verified Access groups across organizational units.
Prerequisites
- AWS account with appropriate IAM permissions
- Identity provider (AWS IAM Identity Center, Okta, or OIDC-compatible)
- Device trust provider (CrowdStrike, Jamf, JumpCloud, or AWS Verified Access native)
- Internal Application Load Balancer (ALB) or network interface endpoint
- Understanding of Cedar policy language
- VPC with application workloads to protect
Architecture
End User (Browser)|| HTTPSv+------+--------+| Verified || Access || Endpoint || (Public DNS) |+------+--------+|+------+--------+| Verified | <-- Cedar Access Policies| Access | <-- Identity Provider Signals| Instance | <-- Device Trust Signals| (Policy || Evaluation) |+------+--------+|+------+--------+| Verified || Access Group || (App Group) |+------+--------+|+------+--------+| Internal ALB || or ENI Target |+------+--------+|+------+--------+| Application || (Private VPC) |+--------------+
Core Components
Verified Access Instance
The regional entity that evaluates access requests against policies.
# Create Verified Access Instance via AWS CLIaws ec2 create-verified-access-instance \--description "Production Zero Trust Instance" \--tag-specifications 'ResourceType=verified-access-instance,Tags=[{Key=Environment,Value=production}]'
Trust Providers
Identity Trust Provider (AWS IAM Identity Center)
# Create identity trust provideraws ec2 create-verified-access-trust-provider \--trust-provider-type user \--user-trust-provider-type iam-identity-center \--policy-reference-name "idc" \--description "IAM Identity Center trust provider" \--tag-specifications 'ResourceType=verified-access-trust-provider,Tags=[{Key=Type,Value=identity}]'
Identity Trust Provider (OIDC - Okta)
aws ec2 create-verified-access-trust-provider \--trust-provider-type user \--user-trust-provider-type oidc \--oidc-options '{"Issuer": "https://company.okta.com/oauth2/default","AuthorizationEndpoint": "https://company.okta.com/oauth2/default/v1/authorize","TokenEndpoint": "https://company.okta.com/oauth2/default/v1/token","UserInfoEndpoint": "https://company.okta.com/oauth2/default/v1/userinfo","ClientId": "0oa1234567890","ClientSecret": "client-secret-here","Scope": "openid profile groups"}' \--policy-reference-name "okta" \--description "Okta OIDC trust provider"
Device Trust Provider (CrowdStrike)
aws ec2 create-verified-access-trust-provider \--trust-provider-type device \--device-trust-provider-type crowdstrike \--device-options '{"TenantId": "crowdstrike-tenant-id","PublicSigningKeyUrl": "https://api.crowdstrike.com/zero-trust/v2/certificates"}' \--policy-reference-name "crowdstrike" \--description "CrowdStrike device trust provider"
Attach Trust Providers to Instance
# Attach identity provideraws ec2 attach-verified-access-trust-provider \--verified-access-instance-id vai-0123456789abcdef \--verified-access-trust-provider-id vatp-0123456789abcdef# Attach device provideraws ec2 attach-verified-access-trust-provider \--verified-access-instance-id vai-0123456789abcdef \--verified-access-trust-provider-id vatp-device123456
Verified Access Groups
# Create a group for web applicationsaws ec2 create-verified-access-group \--verified-access-instance-id vai-0123456789abcdef \--description "Production Web Applications" \--policy-document 'permit(principal, action, resource)when {context.okta.groups.contains("production-access") &&context.crowdstrike.assessment.overall > 50};' \--tag-specifications 'ResourceType=verified-access-group,Tags=[{Key=Tier,Value=web}]'
Verified Access Endpoints
# Create endpoint for ALB-backed applicationaws ec2 create-verified-access-endpoint \--verified-access-group-id vag-0123456789abcdef \--endpoint-type load-balancer \--attachment-type vpc \--domain-certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/xxxx \--application-domain app.internal.company.com \--endpoint-domain-prefix myapp \--load-balancer-options '{"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/internal-alb/xxxx","Port": 443,"Protocol": "https","SubnetIds": ["subnet-abc123", "subnet-def456"]}' \--security-group-ids sg-0123456789abcdef \--description "Internal HR Application"
Cedar Policy Language
Policy Basics
// Allow access for users in the engineering group with compliant devicespermit(principal, action, resource)when {context.okta.groups.contains("engineering") &&context.crowdstrike.assessment.overall > 70 &&context.crowdstrike.assessment.sensor_config.status == "active"};// Deny access from unmanaged devicesforbid(principal, action, resource)when {!context.crowdstrike.assessment.sensor_config.status == "active"};
Advanced Policy Examples
// Time-based access - only during business hours (UTC)permit(principal, action, resource)when {context.okta.groups.contains("contractors") &&context.http_request.http_method == "GET" &&context.crowdstrike.assessment.overall > 80};// Restrict admin access to specific user group with high device trustpermit(principal, action, resource)when {context.idc.groups.contains("admins") &&context.crowdstrike.assessment.overall > 90 &&context.crowdstrike.assessment.os_version.startswith("Windows 11") ||context.crowdstrike.assessment.os_version.startswith("macOS 14")};// Allow read-only access for lower trust levelspermit(principal, action, resource)when {context.okta.groups.contains("read-only") &&context.crowdstrike.assessment.overall > 30 &&context.http_request.http_method == "GET"};
Group-Level vs Endpoint-Level Policies
// Group-level policy (applies to all endpoints in the group)// Set on the Verified Access Grouppermit(principal, action, resource)when {context.okta.groups.contains("employees") &&context.crowdstrike.assessment.overall > 50};// Endpoint-level policy (additional restrictions for specific app)// Set on the Verified Access Endpointpermit(principal, action, resource)when {context.okta.groups.contains("hr-team") &&context.okta.email.endsWith("@company.com")};
Terraform Configuration
terraform {required_providers {aws = {source = "hashicorp/aws"version = "~> 5.0"}}}# Verified Access Instanceresource "aws_verifiedaccess_instance" "main" {description = "Production Zero Trust Access"tags = {Environment = "production"}}# Identity Trust Provider (OIDC)resource "aws_verifiedaccess_trust_provider" "okta" {policy_reference_name = "okta"trust_provider_type = "user"user_trust_provider_type = "oidc"description = "Okta identity provider"oidc_options {authorization_endpoint = "https://company.okta.com/oauth2/default/v1/authorize"client_id = var.okta_client_idclient_secret = var.okta_client_secretissuer = "https://company.okta.com/oauth2/default"scope = "openid profile groups"token_endpoint = "https://company.okta.com/oauth2/default/v1/token"user_info_endpoint = "https://company.okta.com/oauth2/default/v1/userinfo"}}# Device Trust Provider (CrowdStrike)resource "aws_verifiedaccess_trust_provider" "crowdstrike" {policy_reference_name = "crowdstrike"trust_provider_type = "device"device_trust_provider_type = "crowdstrike"description = "CrowdStrike device trust"device_options {tenant_id = var.crowdstrike_tenant_id}}# Attach providers to instanceresource "aws_verifiedaccess_instance_trust_provider_attachment" "okta" {verifiedaccess_instance_id = aws_verifiedaccess_instance.main.idverifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.okta.id}resource "aws_verifiedaccess_instance_trust_provider_attachment" "crowdstrike" {verifiedaccess_instance_id = aws_verifiedaccess_instance.main.idverifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.crowdstrike.id}# Verified Access Groupresource "aws_verifiedaccess_group" "web_apps" {verifiedaccess_instance_id = aws_verifiedaccess_instance.main.iddescription = "Production Web Applications"policy_document = <<-CEDARpermit(principal, action, resource)when {context.okta.groups.contains("production-access") &&context.crowdstrike.assessment.overall > 50};CEDARtags = {Tier = "web"}}# Verified Access Endpointresource "aws_verifiedaccess_endpoint" "internal_app" {verified_access_group_id = aws_verifiedaccess_group.web_apps.idendpoint_type = "load-balancer"attachment_type = "vpc"domain_certificate_arn = aws_acm_certificate.app.arnapplication_domain = "app.internal.company.com"endpoint_domain_prefix = "myapp"description = "Internal Application"load_balancer_options {load_balancer_arn = aws_lb.internal.arnport = 443protocol = "https"subnet_ids = var.private_subnet_ids}security_group_ids = [aws_security_group.verified_access.id]policy_document = <<-CEDARpermit(principal, action, resource)when {context.okta.groups.contains("app-users")};CEDAR}# Logging configurationresource "aws_verifiedaccess_instance_logging_configuration" "main" {verifiedaccess_instance_id = aws_verifiedaccess_instance.main.idaccess_logs {cloudwatch_logs {enabled = truelog_group = aws_cloudwatch_log_group.verified_access.name}s3 {enabled = truebucket_name = aws_s3_bucket.access_logs.idprefix = "verified-access/"}}}resource "aws_cloudwatch_log_group" "verified_access" {name = "/aws/verified-access/production"retention_in_days = 90}
Multi-Account Deployment with AWS RAM
# Share Verified Access Group across accounts via RAMresource "aws_ram_resource_share" "verified_access" {name = "verified-access-share"allow_external_principals = false}resource "aws_ram_resource_association" "group_share" {resource_arn = aws_verifiedaccess_group.web_apps.verified_access_group_arnresource_share_arn = aws_ram_resource_share.verified_access.arn}resource "aws_ram_principal_association" "workload_ou" {principal = "arn:aws:organizations::123456789012:ou/o-xxxx/ou-xxxx-xxxxxxxx"resource_share_arn = aws_ram_resource_share.verified_access.arn}
Monitoring and Logging
# Query access logs in CloudWatchaws logs filter-log-events \--log-group-name /aws/verified-access/production \--filter-pattern '{ $.status_code = "403" }' \--start-time $(date -d '1 hour ago' +%s000)# CloudWatch alarm for access denialsaws cloudwatch put-metric-alarm \--alarm-name "VerifiedAccess-HighDenialRate" \--metric-name "AccessDenied" \--namespace "AWS/VerifiedAccess" \--statistic Sum \--period 300 \--threshold 100 \--comparison-operator GreaterThanThreshold \--evaluation-periods 2 \--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
Security Best Practices
- Layer policies: Use group-level policies for broad controls and endpoint-level for app-specific restrictions
- Require device trust: Always include device posture checks in Cedar policies
- Enable access logging: Send to both CloudWatch and S3 for real-time monitoring and long-term retention
- Use RAM for multi-account: Share groups across OUs instead of duplicating configuration
- Rotate OIDC secrets: Automate client secret rotation via Secrets Manager
- Test policies in non-production: Validate Cedar policies before production deployment
- Set high device trust thresholds: Require overall score above 70 for production access
- Monitor for policy drift: Use AWS Config rules to detect unauthorized changes