Configure AWS Verified Access Endpoints

The aws:verifiedaccess/endpoint:Endpoint resource, part of the Pulumi AWS provider, defines a Verified Access endpoint that enforces identity-based access policies for applications, load balancers, or network ranges. This guide focuses on three capabilities: ALB attachment for distributed applications, direct network interface attachment, and CIDR-based network protection.

Verified Access endpoints belong to Verified Access Groups and reference existing infrastructure such as ALBs, network interfaces, ACM certificates, and VPC resources. The examples are intentionally small. Combine them with your own Verified Access Groups, certificates, and network configuration.

Route traffic through an Application Load Balancer

Most deployments route user traffic through an Application Load Balancer, which distributes requests across backend targets while enforcing access policies at the edge.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.verifiedaccess.Endpoint("example", {
    applicationDomain: "example.com",
    attachmentType: "vpc",
    description: "example",
    domainCertificateArn: exampleAwsAcmCertificate.arn,
    endpointDomainPrefix: "example",
    endpointType: "load-balancer",
    loadBalancerOptions: {
        loadBalancerArn: exampleAwsLb.arn,
        port: 443,
        protocol: "https",
        subnetIds: .map(subnet => (subnet.id)),
    },
    securityGroupIds: [exampleAwsSecurityGroup.id],
    verifiedAccessGroupId: exampleAwsVerifiedaccessGroup.id,
});
import pulumi
import pulumi_aws as aws

example = aws.verifiedaccess.Endpoint("example",
    application_domain="example.com",
    attachment_type="vpc",
    description="example",
    domain_certificate_arn=example_aws_acm_certificate["arn"],
    endpoint_domain_prefix="example",
    endpoint_type="load-balancer",
    load_balancer_options={
        "load_balancer_arn": example_aws_lb["arn"],
        "port": 443,
        "protocol": "https",
        "subnet_ids": [subnet["id"] for subnet in public],
    },
    security_group_ids=[example_aws_security_group["id"]],
    verified_access_group_id=example_aws_verifiedaccess_group["id"])
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.VerifiedAccess.Endpoint("example", new()
    {
        ApplicationDomain = "example.com",
        AttachmentType = "vpc",
        Description = "example",
        DomainCertificateArn = exampleAwsAcmCertificate.Arn,
        EndpointDomainPrefix = "example",
        EndpointType = "load-balancer",
        LoadBalancerOptions = new Aws.VerifiedAccess.Inputs.EndpointLoadBalancerOptionsArgs
        {
            LoadBalancerArn = exampleAwsLb.Arn,
            Port = 443,
            Protocol = "https",
            SubnetIds = .Select(subnet => 
            {
                return subnet.Id;
            }).ToList(),
        },
        SecurityGroupIds = new[]
        {
            exampleAwsSecurityGroup.Id,
        },
        VerifiedAccessGroupId = exampleAwsVerifiedaccessGroup.Id,
    });

});

When users connect to your application domain, Verified Access evaluates their identity against policies before forwarding traffic to the ALB. The endpointType property set to “load-balancer” activates the loadBalancerOptions block, where you specify the ALB ARN, port, and protocol. The applicationDomain and domainCertificateArn properties configure the DNS name users access and the TLS certificate that secures the connection. The verifiedAccessGroupId associates this endpoint with a group that defines trust providers and policies.

Attach directly to a network interface

Some applications run on EC2 instances or containers with specific network interfaces that need direct protection without a load balancer.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.verifiedaccess.Endpoint("example", {
    applicationDomain: "example.com",
    attachmentType: "vpc",
    description: "example",
    domainCertificateArn: exampleAwsAcmCertificate.arn,
    endpointDomainPrefix: "example",
    endpointType: "network-interface",
    networkInterfaceOptions: {
        networkInterfaceId: exampleAwsNetworkInterface.id,
        port: 443,
        protocol: "https",
    },
    securityGroupIds: [exampleAwsSecurityGroup.id],
    verifiedAccessGroupId: exampleAwsVerifiedaccessGroup.id,
});
import pulumi
import pulumi_aws as aws

example = aws.verifiedaccess.Endpoint("example",
    application_domain="example.com",
    attachment_type="vpc",
    description="example",
    domain_certificate_arn=example_aws_acm_certificate["arn"],
    endpoint_domain_prefix="example",
    endpoint_type="network-interface",
    network_interface_options={
        "network_interface_id": example_aws_network_interface["id"],
        "port": 443,
        "protocol": "https",
    },
    security_group_ids=[example_aws_security_group["id"]],
    verified_access_group_id=example_aws_verifiedaccess_group["id"])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/verifiedaccess"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := verifiedaccess.NewEndpoint(ctx, "example", &verifiedaccess.EndpointArgs{
			ApplicationDomain:    pulumi.String("example.com"),
			AttachmentType:       pulumi.String("vpc"),
			Description:          pulumi.String("example"),
			DomainCertificateArn: pulumi.Any(exampleAwsAcmCertificate.Arn),
			EndpointDomainPrefix: pulumi.String("example"),
			EndpointType:         pulumi.String("network-interface"),
			NetworkInterfaceOptions: &verifiedaccess.EndpointNetworkInterfaceOptionsArgs{
				NetworkInterfaceId: pulumi.Any(exampleAwsNetworkInterface.Id),
				Port:               pulumi.Int(443),
				Protocol:           pulumi.String("https"),
			},
			SecurityGroupIds: pulumi.StringArray{
				exampleAwsSecurityGroup.Id,
			},
			VerifiedAccessGroupId: pulumi.Any(exampleAwsVerifiedaccessGroup.Id),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.VerifiedAccess.Endpoint("example", new()
    {
        ApplicationDomain = "example.com",
        AttachmentType = "vpc",
        Description = "example",
        DomainCertificateArn = exampleAwsAcmCertificate.Arn,
        EndpointDomainPrefix = "example",
        EndpointType = "network-interface",
        NetworkInterfaceOptions = new Aws.VerifiedAccess.Inputs.EndpointNetworkInterfaceOptionsArgs
        {
            NetworkInterfaceId = exampleAwsNetworkInterface.Id,
            Port = 443,
            Protocol = "https",
        },
        SecurityGroupIds = new[]
        {
            exampleAwsSecurityGroup.Id,
        },
        VerifiedAccessGroupId = exampleAwsVerifiedaccessGroup.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.verifiedaccess.Endpoint;
import com.pulumi.aws.verifiedaccess.EndpointArgs;
import com.pulumi.aws.verifiedaccess.inputs.EndpointNetworkInterfaceOptionsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Endpoint("example", EndpointArgs.builder()
            .applicationDomain("example.com")
            .attachmentType("vpc")
            .description("example")
            .domainCertificateArn(exampleAwsAcmCertificate.arn())
            .endpointDomainPrefix("example")
            .endpointType("network-interface")
            .networkInterfaceOptions(EndpointNetworkInterfaceOptionsArgs.builder()
                .networkInterfaceId(exampleAwsNetworkInterface.id())
                .port(443)
                .protocol("https")
                .build())
            .securityGroupIds(exampleAwsSecurityGroup.id())
            .verifiedAccessGroupId(exampleAwsVerifiedaccessGroup.id())
            .build());

    }
}
resources:
  example:
    type: aws:verifiedaccess:Endpoint
    properties:
      applicationDomain: example.com
      attachmentType: vpc
      description: example
      domainCertificateArn: ${exampleAwsAcmCertificate.arn}
      endpointDomainPrefix: example
      endpointType: network-interface
      networkInterfaceOptions:
        networkInterfaceId: ${exampleAwsNetworkInterface.id}
        port: 443
        protocol: https
      securityGroupIds:
        - ${exampleAwsSecurityGroup.id}
      verifiedAccessGroupId: ${exampleAwsVerifiedaccessGroup.id}

Setting endpointType to “network-interface” routes traffic directly to a specific ENI. The networkInterfaceOptions block specifies the network interface ID, port, and protocol. Like the ALB configuration, this requires an applicationDomain and domainCertificateArn for DNS and TLS. This pattern works well for single-instance applications or when you need to protect a specific container’s network interface.

Protect CIDR ranges with port-based access

When protecting entire CIDR blocks rather than specific load balancers or instances, you can define port ranges and protocols for network-level access control.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.verifiedaccess.Endpoint("example", {
    attachmentType: "vpc",
    description: "example",
    endpointType: "cidr",
    cidrOptions: {
        cidr: test[0].cidrBlock,
        portRanges: [{
            fromPort: 443,
            toPort: 443,
        }],
        protocol: "tcp",
        subnetIds: .map(subnet => (subnet.id)),
    },
    securityGroupIds: [testAwsSecurityGroup.id],
    verifiedAccessGroupId: testAwsVerifiedaccessGroup.id,
});
import pulumi
import pulumi_aws as aws

example = aws.verifiedaccess.Endpoint("example",
    attachment_type="vpc",
    description="example",
    endpoint_type="cidr",
    cidr_options={
        "cidr": test[0]["cidrBlock"],
        "port_ranges": [{
            "from_port": 443,
            "to_port": 443,
        }],
        "protocol": "tcp",
        "subnet_ids": [subnet["id"] for subnet in test],
    },
    security_group_ids=[test_aws_security_group["id"]],
    verified_access_group_id=test_aws_verifiedaccess_group["id"])
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.VerifiedAccess.Endpoint("example", new()
    {
        AttachmentType = "vpc",
        Description = "example",
        EndpointType = "cidr",
        CidrOptions = new Aws.VerifiedAccess.Inputs.EndpointCidrOptionsArgs
        {
            Cidr = test[0].CidrBlock,
            PortRanges = new[]
            {
                new Aws.VerifiedAccess.Inputs.EndpointCidrOptionsPortRangeArgs
                {
                    FromPort = 443,
                    ToPort = 443,
                },
            },
            Protocol = "tcp",
            SubnetIds = .Select(subnet => 
            {
                return subnet.Id;
            }).ToList(),
        },
        SecurityGroupIds = new[]
        {
            testAwsSecurityGroup.Id,
        },
        VerifiedAccessGroupId = testAwsVerifiedaccessGroup.Id,
    });

});

The “cidr” endpoint type applies identity-based policies to entire subnets or IP ranges. The cidrOptions block specifies the CIDR block, port ranges, protocol, and subnets where the endpoint operates. Unlike load-balancer and network-interface types, CIDR endpoints don’t require an applicationDomain or domainCertificateArn because they operate at the network layer rather than the application layer.

Beyond these examples

These snippets focus on specific endpoint-level features: load balancer and network interface attachment, and CIDR-based network protection. They’re intentionally minimal rather than full access control deployments.

The examples reference pre-existing infrastructure such as Verified Access Groups, ALBs or network interfaces or VPC CIDR blocks, ACM certificates for TLS termination, and VPC subnets and security groups. They focus on configuring the endpoint rather than provisioning the surrounding infrastructure.

To keep things focused, common endpoint patterns are omitted, including:

  • Policy documents for fine-grained authorization (policyDocument)
  • Custom endpoint domain prefixes (endpointDomainPrefix)
  • Server-side encryption configuration (sseSpecification)
  • RDS-specific options (rdsOptions)

These omissions are intentional: the goal is to illustrate how each endpoint type is wired, not provide drop-in access control modules. See the Verified Access Endpoint resource reference for all available configuration options.

Let's configure AWS Verified Access Endpoints

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Endpoint Types & Configuration
What endpoint types are available for Verified Access?
Three types are supported: load-balancer (for Application Load Balancers), network-interface (for Elastic Network Interfaces), and cidr (for CIDR blocks). The type you choose determines which configuration options are required.
What configuration is required for each endpoint type?
For load-balancer and network-interface types, you must provide applicationDomain, domainCertificateArn, and type-specific options (loadBalancerOptions or networkInterfaceOptions). For cidr type, you only need cidrOptions (no domain or certificate required). All types require attachmentType set to vpc, verifiedAccessGroupId, and securityGroupIds.
When should I use each endpoint type?
Use load-balancer when fronting an Application Load Balancer, network-interface when connecting directly to an ENI, and cidr when defining access to a CIDR block range. The load-balancer and network-interface types require TLS certificates and custom domains, while cidr does not.
What's the only supported attachment type?
Currently, only vpc is supported for attachmentType.
Certificates & Domains
What certificate requirements exist for Verified Access endpoints?
For load-balancer and network-interface endpoint types, you must provide a domainCertificateArn pointing to an ACM certificate. The certificate’s Common Name (CN) must match the DNS name your end users will use to reach your application (specified in applicationDomain).
What's the difference between applicationDomain and endpointDomain?
applicationDomain is the DNS name you configure for users to reach your application (required for load-balancer and network-interface types). endpointDomain is a computed output—a DNS name automatically generated by AWS for the endpoint.
Immutability & Updates
What properties can't be changed after creating an endpoint?
The following properties are immutable and require resource replacement if changed: attachmentType, endpointType, applicationDomain, domainCertificateArn, endpointDomainPrefix, and securityGroupIds. Plan these values carefully during initial creation.
What does the deviceValidationDomain output indicate?
This computed field is returned only if the endpoint has a device trust provider attached, indicating device validation is configured.

Using a different cloud?

Explore security guides for other cloud providers: