writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
configuring-aws-verified-access-for-ztnaConfigure AWS Verified Access to provide VPN-less zero trust network access to internal apps, combining identity trust providers (IAM Identity Center, Okta/OIDC), device posture providers (CrowdStrike, Jamf), Cedar policy authoring, and Terraform deployment. Use when replacing VPN access with ZTNA, writing Cedar access policies, or deploying Verified Access…
Independent GitHub snapshot · Automated classification · Original documentation governs
name: configuring-aws-verified-access-for-ztna description: Configure AWS Verified Access to provide VPN-less zero trust network access to internal apps, combining identity trust providers (IAM Identity Center, Okta/OIDC), device posture providers (CrowdStrike, Jamf), Cedar policy authoring, and Terraform deployment. Use when replacing VPN access with ZTNA, writing Cedar access policies, or deploying Verified Access instances, groups, and endpoints across AWS accounts. domain: cybersecurity subdomain: zero-trust-architecture tags:
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.
End User (Browser)
|
| HTTPS
v
+------+--------+
| 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) |
+--------------+
The regional entity that evaluates access requests against policies.
# Create Verified Access Instance via AWS CLI
aws ec2 create-verified-access-instance \
--description "Production Zero Trust Instance" \
--tag-specifications 'ResourceType=verified-access-instance,Tags=[{Key=Environment,Value=production}]'
# Create identity trust provider
aws 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}]'
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"
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 identity provider
aws ec2 attach-verified-access-trust-provider \
--verified-access-instance-id vai-0123456789abcdef \
--verified-access-trust-provider-id vatp-0123456789abcdef
# Attach device provider
aws ec2 attach-verified-access-trust-provider \
--verified-access-instance-id vai-0123456789abcdef \
--verified-access-trust-provider-id vatp-device123456
# Create a group for web applications
aws 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}]'
# Create endpoint for ALB-backed application
aws 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"
// Allow access for users in the engineering group with compliant devices
permit(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 devices
forbid(principal, action, resource)
when {
!context.crowdstrike.assessment.sensor_config.status == "active"
};
// 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 trust
permit(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 levels
permit(principal, action, resource)
when {
context.okta.groups.contains("read-only") &&
context.crowdstrike.assessment.overall > 30 &&
context.http_request.http_method == "GET"
};
// Group-level policy (applies to all endpoints in the group)
// Set on the Verified Access Group
permit(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 Endpoint
permit(principal, action, resource)
when {
context.okta.groups.contains("hr-team") &&
context.okta.email.endsWith("@company.com")
};
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Verified Access Instance
resource "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_id
client_secret = var.okta_client_secret
issuer = "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 instance
resource "aws_verifiedaccess_instance_trust_provider_attachment" "okta" {
verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id
verifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.okta.id
}
resource "aws_verifiedaccess_instance_trust_provider_attachment" "crowdstrike" {
verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id
verifiedaccess_trust_provider_id = aws_verifiedaccess_trust_provider.crowdstrike.id
}
# Verified Access Group
resource "aws_verifiedaccess_group" "web_apps" {
verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id
description = "Production Web Applications"
policy_document = <<-CEDAR
permit(principal, action, resource)
when {
context.okta.groups.contains("production-access") &&
context.crowdstrike.assessment.overall > 50
};
CEDAR
tags = {
Tier = "web"
}
}
# Verified Access Endpoint
resource "aws_verifiedaccess_endpoint" "internal_app" {
verified_access_group_id = aws_verifiedaccess_group.web_apps.id
endpoint_type = "load-balancer"
attachment_type = "vpc"
domain_certificate_arn = aws_acm_certificate.app.arn
application_domain = "app.internal.company.com"
endpoint_domain_prefix = "myapp"
description = "Internal Application"
load_balancer_options {
load_balancer_arn = aws_lb.internal.arn
port = 443
protocol = "https"
subnet_ids = var.private_subnet_ids
}
security_group_ids = [aws_security_group.verified_access.id]
policy_document = <<-CEDAR
permit(principal, action, resource)
when {
context.okta.groups.contains("app-users")
};
CEDAR
}
# Logging configuration
resource "aws_verifiedaccess_instance_logging_configuration" "main" {
verifiedaccess_instance_id = aws_verifiedaccess_instance.main.id
access_logs {
cloudwatch_logs {
enabled = true
log_group = aws_cloudwatch_log_group.verified_access.name
}
s3 {
enabled = true
bucket_name = aws_s3_bucket.access_logs.id
prefix = "verified-access/"
}
}
}
resource "aws_cloudwatch_log_group" "verified_access" {
name = "/aws/verified-access/production"
retention_in_days = 90
}
# Share Verified Access Group across accounts via RAM
resource "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_arn
resource_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
}
# Query access logs in CloudWatch
aws 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 denials
aws 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