Skip to content

PrivateLink implementation guide for a third-party SaaS integration

Establishing private connectivity between a company VPC and a third-party SaaS application over AWS PrivateLink. The result is API access that never crosses the public internet and needs no shared address space.

graph TD subgraph "Company VPC" A[Company Application] -->|Request| B[Security Group] B -->|Secured Traffic| C[Interface VPC Endpoint] subgraph "Company Subnet" A B C end end subgraph "AWS PrivateLink" C -->|Private Connection| D[AWS PrivateLink Service] end subgraph "SaaS Provider VPC" D -->|Secured Traffic| E[Endpoint Service] E -->|Filtered Traffic| F[Security Group] F -->|API Request| G[SaaS Application] subgraph "Provider Subnet" E F G end end classDef vpc fill:#e6f3ff,stroke:#333,stroke-width:2px classDef security fill:#ffe6e6,stroke:#333,stroke-width:2px classDef endpoint fill:#e6ffe6,stroke:#333,stroke-width:2px classDef application fill:#fff2cc,stroke:#333,stroke-width:2px class A,G application class B,F security class C,E endpoint class D vpc
  • An AWS account with permission to create VPC endpoints.
  • An existing VPC with private subnets in at least two Availability Zones.
  • The provider’s service name, in the form com.amazonaws.vpce.<region>.vpce-svc-xxxxxxxx.
  • The list of API endpoints and ports the application will use.
  • Confirmation from the provider that they have allow-listed your account as a principal on their endpoint service.
  • The AWS CLI configured.
  • Confirm the endpoint service exists in the same Region as your VPC, or that a cross-Region endpoint is supported for it.
  • Verify each subnet has free IP addresses for an endpoint network interface.
  • Document the API endpoints and ports required.
  • Decide whether private DNS will be used, or whether the application will be pointed at the endpoint-specific DNS name.

Overlapping CIDR blocks are not a concern here — that is one of the reasons to use PrivateLink rather than peering.

Terminal window
aws ec2 create-security-group \
--group-name "privatelink-endpoint-sg" \
--description "Security group for PrivateLink endpoint" \
--vpc-id "vpc-xxxxx"
aws ec2 authorize-security-group-ingress \
--group-id "sg-xxxxx" \
--protocol tcp \
--port 443 \
--cidr 10.0.0.0/16

Allow only the ports the API uses, and only from the address ranges or security groups that should reach it. This security group applies to the endpoint network interfaces, so it is the first place traffic is filtered.

Terminal window
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxxxx \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.vpce.eu-west-2.vpce-svc-xxxxx \
--subnet-ids subnet-xxxxx subnet-yyyyy \
--security-group-ids sg-xxxxx \
--private-dns-enabled true

Choose one subnet per Availability Zone that the provider’s endpoint service supports. The endpoint enters pendingAcceptance until the provider accepts the connection request, unless they have enabled automatic acceptance.

With private DNS enabled, the provider’s public hostname resolves inside your VPC to the endpoint’s private addresses, and the application needs no configuration change. This requires enableDnsSupport and enableDnsHostnames on the VPC.

Without it, use the endpoint-specific DNS name that AWS generates, or point a Route 53 private hosted zone record at it.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ManagePrivateLinkEndpoints",
"Effect": "Allow",
"Action": [
"ec2:DescribeVpcEndpoints",
"ec2:CreateVpcEndpoint",
"ec2:DeleteVpcEndpoints",
"ec2:ModifyVpcEndpoint"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["eu-west-2"]
}
}
}
]
}

Separately, attach an endpoint policy to the endpoint itself if the service supports one, to restrict which principals and actions may use it.

Terminal window
# Resolution should return a private address from your subnet range
nslookup api.saasprovider.example
# Then the API itself
curl -sS -o /dev/null -w '%{http_code}\n' https://api.saasprovider.example/health

Check off:

  • DNS resolves to a private address inside the VPC, not a public one.
  • API calls succeed through the endpoint.
  • No traffic reaches the internet gateway or NAT gateway for this destination — confirm in VPC flow logs.
  • The endpoint reports available and its CloudWatch metrics show traffic.

Interface endpoints do not answer ICMP, so ping proves nothing. Use nc or nmap if you need a connectivity test below the application layer.

AWS PrivateLink publishes endpoint metrics in the AWS/PrivateLinkEndpoints namespace:

MetricWhat it tells you
ActiveConnectionsConcurrent connections through the endpoint
NewConnectionsConnections established in the period
BytesProcessedBytes in both directions — this is what you are billed on
PacketsDroppedPackets the endpoint dropped; a rising value suggests the endpoint or the service is unhealthy
RstPacketsReceivedRST packets received; a rising value suggests the endpoint service is unhealthy

Dimensions are Endpoint Type, Service Name, VPC Endpoint Id, VPC Id and, at finer granularity, Subnet Id.

If you also publish an endpoint service, its metrics are in AWS/PrivateLinkServices.

An alarm on dropped packets:

Terminal window
aws cloudwatch put-metric-alarm \
--alarm-name "privatelink-endpoint-packets-dropped" \
--alarm-description "PrivateLink endpoint is dropping packets" \
--namespace "AWS/PrivateLinkEndpoints" \
--metric-name "PacketsDropped" \
--dimensions Name="VPC Endpoint Id",Value=vpce-xxxxx \
--statistic "Sum" \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions "arn:aws:sns:eu-west-2:<account-id>:<topic-name>"

Metrics are published for interface endpoints, Gateway Load Balancer endpoints and endpoint services. They are not published for gateway endpoints, so an alarm on an S3 gateway endpoint will never fire.

  • Review the endpoint security group rules on the same schedule as any other production control.
  • Use an endpoint policy for fine-grained control where the service supports one.
  • Enable VPC flow logs on the endpoint subnets.
  • Use IAM roles rather than long-lived access keys for anything calling the API.
  • Audit which accounts hold endpoints against the provider’s service, and ask the provider to remove principals you no longer use.
SymptomLook at
Endpoint stuck in pendingAcceptanceThe provider has not accepted the connection, or your account is not on their allow list
DNS returns a public addressPrivate DNS not enabled; enableDnsSupport/enableDnsHostnames off; a conflicting Route 53 record
Connection times outEndpoint security group; network ACLs on the endpoint subnets; no endpoint interface in the caller’s Availability Zone
Intermittent failuresPacketsDropped and RstPacketsReceived; provider-side target health
ping fails but the API worksExpected — interface endpoints do not respond to ICMP
  • Per endpoint-hour, per Availability Zone.
  • Per gigabyte processed.
  • Cross-Availability-Zone data transfer if callers and endpoint interfaces are in different zones — put an interface in every zone that has callers.