IAM to VPC endpoints: the extra policy on the network path

A request through a VPC endpoint is checked by the endpoint's policy as well as IAM and resource policies. What each side expects, the mismatches that bite, a known-good configuration, the known-bad ones and their symptoms, and how to tell which side refused.

markdownpatternv4TGER-J63Astage: buildtrust: officialservice: iamservice: vpcinterface: cliinterface: iacVPC endpoint policygateway endpointinterface endpointPrivateLinkaws:SourceVpceaws:SourceIpVPC endpoint policy access deniedmodify-vpc-endpoint

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

IAM to VPC endpoints

When traffic to an AWS service goes through a VPC endpoint, the endpoint's policy is one more layer that must allow the request. It never grants anything: the caller's identity policy and any resource policy must still allow. With no policy set, AWS attaches a full-access default, so endpoint denials only start when someone narrows it.

What crosses the handoff

SideEmits / expects
Caller (IAM identity in the VPC)A signed request for an action on a resource, routed through the endpoint because of VPC routing (gateway endpoints) or DNS (interface endpoints). It carries the caller's principal and, because it used the endpoint, the endpoint ID as aws:SourceVpce.
VPC endpointOne JSON policy per interface or gateway endpoint for an AWS service. It must contain a Principal element and be at most 20,480 characters including white space. It is evaluated against the principal, action and resource of each request passing through.
Target resource (bucket, queue, secret)Its own resource policy, which can require the endpoint by testing aws:SourceVpce, or the VPC by testing aws:SourceVpc.

Endpoints for services that are not AWS services (another account's service, Marketplace) have no policy and full access. Not every AWS service supports endpoint policies; where unsupported, the endpoint allows everything.

Mismatches that bite

  • aws:SourceIp disappears. It is absent when the request travels through a VPC endpoint. A bucket policy that allows by office or NAT IP range stops matching as soon as an endpoint is added: an Allow keyed on it no longer applies, and a Deny written with a negated IP operator now matches and denies.
  • aws:SourceVpce appears only on the endpoint path. It is present only when the requester used a VPC endpoint. A resource policy that requires a specific endpoint therefore denies every request made without that endpoint, including requests from the console.
  • Account principals in a gateway endpoint policy. For gateway endpoints the Principal must be "*"; to limit who can use it, test aws:PrincipalArn in a condition. Writing "AWS": "<account-id>" grants only that account's root user, not the users and roles in it.
  • Wildcards and system keys. * and ? and numeric operators cannot be used with system-generated global keys such as aws:PrincipalAccount or aws:SourceVpc in an endpoint policy. A string wildcard needs at least six consecutive characters before or after it. An ARN's account field is either an account ID or a wildcard, never both.
  • Propagation. A changed endpoint policy can take a few minutes to take effect; test again before concluding a fix did not work.
  • CloudTrail gaps. An access denial from an endpoint policy may not appear in CloudTrail when the endpoint belongs to a different account than the caller or the target role.

Permissions on each side

  • Caller: its identity-based policy must allow the action on the resource, exactly as without the endpoint.
  • Endpoint owner: whoever runs aws ec2 modify-vpc-endpoint --vpc-endpoint-id <id> --policy-document file://endpoint-policy.json needs permission to modify the endpoint in the VPC's account.
  • Resource owner: controls the resource policy, including any aws:SourceVpce / aws:SourceVpc requirement.

Known-good

A gateway endpoint that only one role may use, combined with a bucket that only accepts requests through that endpoint:

{
  "Statement": [{
    "Effect": "Allow",
    "Principal": "*",
    "Action": "*",
    "Resource": "*",
    "Condition": { "StringEquals": { "aws:PrincipalArn": "arn:aws:iam::<account-id>:role/<app-role>" } }
  }]
}

On the bucket, a Deny for requests that did not come through vpce-<id>, tested with StringNotEquals on aws:SourceVpce, scoped so that it does not lock out the administrators who must manage the bucket from outside the VPC. Write that exception before applying the Deny.

Known-bad

ConfigurationSymptom
Endpoint policy narrowed to a list of actions that omits one the application usesUser: <arn> is not authorized to perform: <action> … because no VPC endpoint policy allows the <action> action
Endpoint policy with an explicit Deny on an action… with an explicit deny in a VPC endpoint policy
Gateway endpoint policy with "Principal": {"AWS": "<account-id>"}Every role and user in that account is refused through the endpoint (only the account root user is granted)
Bucket policy requiring aws:SourceVpce with no exception for administratorsConsole and out-of-VPC access to the bucket is denied, including for administrators; the message names the resource-based policy, not the endpoint
Bucket policy allowing by aws:SourceIp after an endpoint is addedRequests from inside the VPC are refused by the bucket policy, though nothing about the caller changed

Which side refused?

  • Message names a VPC endpoint policy → the endpoint. Read its policy with aws ec2 describe-vpc-endpoints --vpc-endpoint-ids <id> (UNVERIFIED command form) and compare it with the action and principal.
  • Message names a resource-based policy → the target's own policy, usually an aws:SourceVpce, aws:SourceVpc or aws:SourceIp condition; compare the condition with the path the request took.
  • Message names an identity-based policy → the caller; the endpoint is not involved.
  • No denial in CloudTrail and the endpoint is in another account → suspect the endpoint first.

Read this document as markdown — the same bytes an agent receives.