Repository: cloud-custodian/cloud-custodian

Issue:
AWS - vpc-endpoint filter for vpc resource
**Is your feature request related to a problem? Please describe.**
Yes, we're trying to filter vpc based on their vpc endpoints. for example filter vpc's that have a s3 vpc endpoint

**Describe the solution you'd like**
I am thinking of creating a related resource filter that filters vpcs based on their vpc endpoints. 

for example 
```
policies:
  - name: s3-vpc-endpoint-enabled
    resource: vpc
    filters:
       - type: vpc-endpoint
          key: ServiceName
          value: com.amazonaws.us-east-1.s3
```

**Additional context**
The goal is to ensure certain vpc's contain certain vpc endpoints


Discussion:

Resolving patch:
diff --git a/c7n/filters/related.py b/c7n/filters/related.py
--- a/c7n/filters/related.py
+++ b/c7n/filters/related.py
@@ -111,3 +111,65 @@ def _add_annotations(self, related_ids, resource):
     def process(self, resources, event=None):
         related = self.get_related(resources)
         return [r for r in resources if self.process_resource(r, related)]
+
+
+class RelatedResourceByIdFilter(RelatedResourceFilter):
+    """
+    Value filter for related resources in which the main resource only contains the related
+    resource id.
+    """
+
+    RelatedResourceByIdExpression = None
+
+    def get_related(self, resources):
+        resource_manager = self.get_resource_manager()
+        related_ids = self.get_related_ids(resources)
+
+        related = {}
+        for r in resource_manager.resources():
+            matched_vpc = self.get_related_by_ids(r) & related_ids
+            if matched_vpc:
+                for vpc in matched_vpc:
+                    related_resources = related.get(vpc, [])
+                    related_resources.append(r)
+                    related[vpc] = related_resources
+        return related
+
+    def get_related_by_ids(self, resources):
+        RelatedResourceKey = self.RelatedResourceByIdExpression or self.RelatedIdsExpression
+        ids = jmespath.search("%s" % RelatedResourceKey, resources)
+        if isinstance(ids, str):
+            ids = [ids]
+        return set(ids)
+
+    def process_resource(self, resource, related):
+        related_ids = self.get_related_ids([resource])
+        op = self.data.get('operator', 'or')
+        found = []
+
+        if self.data.get('match-resource') is True:
+            self.data['value'] = self.get_resource_value(
+                self.data['key'], resource)
+
+        if self.data.get('value_type') == 'resource_count':
+            count_matches = OPERATORS[self.data.get('op')](len(related_ids), self.data.get('value'))
+            if count_matches:
+                self._add_annotations(related_ids, resource)
+            return count_matches
+
+        for rid in related_ids:
+            robjs = related.get(rid, [None])
+            for robj in robjs:
+                if robj is None:
+                    continue
+                if self.match(robj):
+                    found.append(rid)
+
+        if found:
+            self._add_annotations(found, resource)
+
+        if op == 'or' and found:
+            return True
+        elif op == 'and' and len(found) == len(related_ids):
+            return True
+        return False
diff --git a/c7n/resources/vpc.py b/c7n/resources/vpc.py
--- a/c7n/resources/vpc.py
+++ b/c7n/resources/vpc.py
@@ -22,7 +22,7 @@
     DefaultVpcBase, Filter, ValueFilter)
 import c7n.filters.vpc as net_filters
 from c7n.filters.iamaccess import CrossAccountAccessFilter
-from c7n.filters.related import RelatedResourceFilter
+from c7n.filters.related import RelatedResourceFilter, RelatedResourceByIdFilter
 from c7n.filters.revisions import Diff
 from c7n import query, resolver
 from c7n.manager import resources
@@ -2173,6 +2173,57 @@ class EndpointVpcFilter(net_filters.VpcFilter):
     RelatedIdsExpression = "VpcId"
 
 
+@Vpc.filter_registry.register("vpc-endpoint")
+class VPCEndpointFilter(RelatedResourceByIdFilter):
+    """Filters vpcs based on their vpc-endpoints
+
+    :example:
+
+    .. code-block:: yaml
+
+            policies:
+              - name: s3-vpc-endpoint-enabled
+                resource: vpc
+                filters:
+                  - type: vpc-endpoint
+                    key: ServiceName
+                    value: com.amazonaws.us-east-1.s3
+    """
+    RelatedResource = "c7n.resources.vpc.VpcEndpoint"
+    RelatedIdsExpression = "VpcId"
+    AnnotationKey = "matched-vpc-endpoint"
+
+    schema = type_schema(
+        'vpc-endpoint',
+        rinherit=ValueFilter.schema)
+
+
+@Subnet.filter_registry.register("vpc-endpoint")
+class SubnetEndpointFilter(RelatedResourceByIdFilter):
+    """Filters subnets based on their vpc-endpoints
+
+    :example:
+
+    .. code-block:: yaml
+
+            policies:
+              - name: athena-endpoint-enabled
+                resource: subnet
+                filters:
+                  - type: vpc-endpoint
+                    key: ServiceName
+                    value: com.amazonaws.us-east-1.athena
+    """
+    RelatedResource = "c7n.resources.vpc.VpcEndpoint"
+    RelatedIdsExpression = "SubnetId"
+    RelatedResourceByIdExpression = "SubnetIds"
+    AnnotationKey = "matched-vpc-endpoint"
+
+    schema = type_schema(
+        'vpc-endpoint',
+        rinherit=ValueFilter.schema)
+
+
 @resources.register('key-pair')
 class KeyPair(query.QueryResourceManager):
 


Test patch:
diff --git a/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeSubnets_1.json b/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeSubnets_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeSubnets_1.json
@@ -0,0 +1,362 @@
+{
+    "status_code": 200,
+    "data": {
+        "Subnets": [
+            {
+                "AvailabilityZone": "us-east-1a",
+                "AvailabilityZoneId": "use1-az4",
+                "AvailableIpAddressCount": 249,
+                "CidrBlock": "10.0.1.0/24",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": false,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-068dfbf3f275a6ae8",
+                "VpcId": "vpc-072f438c953672ace",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "Private subnet1"
+                    },
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Customer"
+                    },
+                    {
+                        "Key": "kubernetes.io/cluster/test",
+                        "Value": "shared"
+                    },
+                    {
+                        "Key": "kubernetes.io/cluster/test2",
+                        "Value": "shared"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-068dfbf3f275a6ae8"
+            },
+            {
+                "AvailabilityZone": "us-east-1d",
+                "AvailabilityZoneId": "use1-az2",
+                "AvailableIpAddressCount": 4086,
+                "CidrBlock": "172.31.48.0/20",
+                "DefaultForAz": true,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-3a334610",
+                "VpcId": "vpc-d2d616b5",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Customer"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "Default-48"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-3a334610"
+            },
+            {
+                "AvailabilityZone": "us-east-1b",
+                "AvailabilityZoneId": "use1-az6",
+                "AvailableIpAddressCount": 4088,
+                "CidrBlock": "172.31.16.0/20",
+                "DefaultForAz": true,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-efbcccb7",
+                "VpcId": "vpc-d2d616b5",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Public"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": ""
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-efbcccb7"
+            },
+            {
+                "AvailabilityZone": "us-east-1c",
+                "AvailabilityZoneId": "use1-az1",
+                "AvailableIpAddressCount": 8186,
+                "CidrBlock": "192.168.0.0/19",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-0b7a8f2c1d0f20592",
+                "VpcId": "vpc-01a1ffc078406ab62",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "alpha.eksctl.io/cluster-name",
+                        "Value": "sanjay"
+                    },
+                    {
+                        "Key": "aws:cloudformation:stack-name",
+                        "Value": "eksctl-sanjay-cluster"
+                    },
+                    {
+                        "Key": "kubernetes.io/role/elb",
+                        "Value": "1"
+                    },
+                    {
+                        "Key": "alpha.eksctl.io/eksctl-version",
+                        "Value": "0.18.0"
+                    },
+                    {
+                        "Key": "DontTurnOff",
+                        "Value": "CloudCustodian"
+                    },
+                    {
+                        "Key": "aws:cloudformation:logical-id",
+                        "Value": "SubnetPublicUSEAST1C"
+                    },
+                    {
+                        "Key": "eksctl.cluster.k8s.io/v1alpha1/cluster-name",
+                        "Value": "sanjay"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "eksctl-sanjay-cluster/SubnetPublicUSEAST1C"
+                    },
+                    {
+                        "Key": "aws:cloudformation:stack-id",
+                        "Value": "arn:aws:cloudformation:us-east-1:644160558196:stack/eksctl-sanjay-cluster/ad933be0-88bf-11ea-8212-1208f0f76cbf"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-0b7a8f2c1d0f20592"
+            },
+            {
+                "AvailabilityZone": "us-east-1b",
+                "AvailabilityZoneId": "use1-az6",
+                "AvailableIpAddressCount": 251,
+                "CidrBlock": "10.0.0.0/24",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": false,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-05b58b4afe5124322",
+                "VpcId": "vpc-0a59f94e3fca9fbc0",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "implied"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-05b58b4afe5124322"
+            },
+            {
+                "AvailabilityZone": "us-east-1a",
+                "AvailabilityZoneId": "use1-az4",
+                "AvailableIpAddressCount": 4090,
+                "CidrBlock": "10.205.0.0/20",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-07c118e47bb84cee7",
+                "VpcId": "vpc-03005fb9b8740263d",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Public"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "testing-subnet"
+                    },
+                    {
+                        "Key": "Owner",
+                        "Value": "faizan.ahmed@capitalone.com"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-07c118e47bb84cee7"
+            },
+            {
+                "AvailabilityZone": "us-east-1e",
+                "AvailabilityZoneId": "use1-az3",
+                "AvailableIpAddressCount": 4090,
+                "CidrBlock": "172.31.32.0/20",
+                "DefaultForAz": true,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-e3b194de",
+                "VpcId": "vpc-d2d616b5",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Public"
+                    },
+                    {
+                        "Key": "TestTag",
+                        "Value": "TestValue"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-e3b194de"
+            },
+            {
+                "AvailabilityZone": "us-east-1a",
+                "AvailabilityZoneId": "use1-az4",
+                "AvailableIpAddressCount": 248,
+                "CidrBlock": "10.0.0.0/24",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-023db907202d61c00",
+                "VpcId": "vpc-072f438c953672ace",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "kubernetes.io/cluster/test",
+                        "Value": "shared"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "Public subnet1"
+                    },
+                    {
+                        "Key": "kubernetes.io/cluster/test2",
+                        "Value": "shared"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-023db907202d61c00"
+            },
+            {
+                "AvailabilityZone": "us-east-1b",
+                "AvailabilityZoneId": "use1-az6",
+                "AvailableIpAddressCount": 247,
+                "CidrBlock": "10.0.2.0/24",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": false,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-03ec1a55eadb55221",
+                "VpcId": "vpc-072f438c953672ace",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "Private Subnet2"
+                    },
+                    {
+                        "Key": "kubernetes.io/cluster/test2",
+                        "Value": "shared"
+                    },
+                    {
+                        "Key": "kubernetes.io/cluster/test",
+                        "Value": "shared"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-03ec1a55eadb55221"
+            },
+            {
+                "AvailabilityZone": "us-east-1c",
+                "AvailabilityZoneId": "use1-az1",
+                "AvailableIpAddressCount": 8187,
+                "CidrBlock": "192.168.64.0/19",
+                "DefaultForAz": false,
+                "MapPublicIpOnLaunch": false,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-015ee3a07508b8409",
+                "VpcId": "vpc-01a1ffc078406ab62",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "eksctl-sanjay-cluster/SubnetPrivateUSEAST1C"
+                    },
+                    {
+                        "Key": "eksctl.cluster.k8s.io/v1alpha1/cluster-name",
+                        "Value": "sanjay"
+                    },
+                    {
+                        "Key": "kubernetes.io/role/internal-elb",
+                        "Value": "1"
+                    },
+                    {
+                        "Key": "DontTurnOff",
+                        "Value": "CloudCustodian"
+                    },
+                    {
+                        "Key": "alpha.eksctl.io/eksctl-version",
+                        "Value": "0.18.0"
+                    },
+                    {
+                        "Key": "alpha.eksctl.io/cluster-name",
+                        "Value": "sanjay"
+                    },
+                    {
+                        "Key": "aws:cloudformation:stack-name",
+                        "Value": "eksctl-sanjay-cluster"
+                    },
+                    {
+                        "Key": "aws:cloudformation:stack-id",
+                        "Value": "arn:aws:cloudformation:us-east-1:644160558196:stack/eksctl-sanjay-cluster/ad933be0-88bf-11ea-8212-1208f0f76cbf"
+                    },
+                    {
+                        "Key": "aws:cloudformation:logical-id",
+                        "Value": "SubnetPrivateUSEAST1C"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-015ee3a07508b8409"
+            },
+            {
+                "AvailabilityZone": "us-east-1a",
+                "AvailabilityZoneId": "use1-az4",
+                "AvailableIpAddressCount": 4088,
+                "CidrBlock": "172.31.0.0/20",
+                "DefaultForAz": true,
+                "MapPublicIpOnLaunch": true,
+                "MapCustomerOwnedIpOnLaunch": false,
+                "State": "available",
+                "SubnetId": "subnet-914763e7",
+                "VpcId": "vpc-d2d616b5",
+                "OwnerId": "644160558196",
+                "AssignIpv6AddressOnCreation": false,
+                "Ipv6CidrBlockAssociationSet": [],
+                "Tags": [
+                    {
+                        "Key": "NetworkLocation",
+                        "Value": "Public"
+                    }
+                ],
+                "SubnetArn": "arn:aws:ec2:us-east-1:644160558196:subnet/subnet-914763e7"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeVpcEndpoints_1.json b/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeVpcEndpoints_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_subnet_endpoint_filter/ec2.DescribeVpcEndpoints_1.json
@@ -0,0 +1,145 @@
+{
+    "status_code": 200,
+    "data": {
+        "VpcEndpoints": [
+            {
+                "VpcEndpointId": "vpce-04a4c8e2145cb966e",
+                "VpcEndpointType": "Interface",
+                "VpcId": "vpc-d2d616b5",
+                "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-0caf1c7342f032f18",
+                "State": "available",
+                "PolicyDocument": "{\n  \"Statement\": [\n    {\n      \"Action\": \"*\", \n      \"Effect\": \"Allow\", \n      \"Principal\": \"*\", \n      \"Resource\": \"*\"\n    }\n  ]\n}",
+                "RouteTableIds": [],
+                "SubnetIds": [
+                    "subnet-3a334610",
+                    "subnet-efbcccb7"
+                ],
+                "Groups": [
+                    {
+                        "GroupId": "sg-6c7fa917",
+                        "GroupName": "default"
+                    }
+                ],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": true,
+                "NetworkInterfaceIds": [
+                    "eni-009c64280f9096586",
+                    "eni-6441605581967e01c"
+                ],
+                "DnsEntries": [
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i-us-east-1d.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i-us-east-1b.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    }
+                ],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2019,
+                    "month": 10,
+                    "day": 20,
+                    "hour": 10,
+                    "minute": 53,
+                    "second": 39,
+                    "microsecond": 0
+                },
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "c7n"
+                    }
+                ],
+                "OwnerId": "644160558196"
+            },
+            {
+                "VpcEndpointId": "vpce-0bc8bcfcc74fa2db9",
+                "VpcEndpointType": "Gateway",
+                "VpcId": "vpc-072f438c953672ace",
+                "ServiceName": "com.amazonaws.us-east-1.s3",
+                "State": "available",
+                "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
+                "RouteTableIds": [
+                    "rtb-0b0e2aef9dd3d7580",
+                    "rtb-09e9ec4e3854addd7"
+                ],
+                "SubnetIds": [],
+                "Groups": [],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": false,
+                "NetworkInterfaceIds": [],
+                "DnsEntries": [],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 4,
+                    "day": 28,
+                    "hour": 20,
+                    "minute": 52,
+                    "second": 11,
+                    "microsecond": 0
+                },
+                "Tags": [],
+                "OwnerId": "644160558196"
+            },
+            {
+                "VpcEndpointId": "vpce-08ff0494faf0210d3",
+                "VpcEndpointType": "Interface",
+                "VpcId": "vpc-072f438c953672ace",
+                "ServiceName": "com.amazonaws.us-east-1.athena",
+                "State": "available",
+                "PolicyDocument": "{\n    \"Statement\": [\n        {\n            \"Action\": \"*\",\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\",\n            \"Principal\": \"*\"\n        }\n    ]\n}",
+                "RouteTableIds": [],
+                "SubnetIds": [
+                    "subnet-068dfbf3f275a6ae8",
+                    "subnet-03ec1a55eadb55221"
+                ],
+                "Groups": [
+                    {
+                        "GroupId": "sg-0d30141b566cfa039",
+                        "GroupName": "default"
+                    }
+                ],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": false,
+                "NetworkInterfaceIds": [
+                    "eni-01704d489bb6aeb98",
+                    "eni-029615f8fb7a39561"
+                ],
+                "DnsEntries": [
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by-us-east-1a.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by-us-east-1b.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    }
+                ],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 7,
+                    "day": 7,
+                    "hour": 19,
+                    "minute": 25,
+                    "second": 38,
+                    "microsecond": 0
+                },
+                "Tags": [],
+                "OwnerId": "644160558196"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcEndpoints_1.json b/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcEndpoints_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcEndpoints_1.json
@@ -0,0 +1,145 @@
+{
+    "status_code": 200,
+    "data": {
+        "VpcEndpoints": [
+            {
+                "VpcEndpointId": "vpce-04a4c8e2145cb966e",
+                "VpcEndpointType": "Interface",
+                "VpcId": "vpc-d2d616b5",
+                "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-0caf1c7342f032f18",
+                "State": "available",
+                "PolicyDocument": "{\n  \"Statement\": [\n    {\n      \"Action\": \"*\", \n      \"Effect\": \"Allow\", \n      \"Principal\": \"*\", \n      \"Resource\": \"*\"\n    }\n  ]\n}",
+                "RouteTableIds": [],
+                "SubnetIds": [
+                    "subnet-3a334610",
+                    "subnet-efbcccb7"
+                ],
+                "Groups": [
+                    {
+                        "GroupId": "sg-6c7fa917",
+                        "GroupName": "default"
+                    }
+                ],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": true,
+                "NetworkInterfaceIds": [
+                    "eni-009c64280f9096586",
+                    "eni-6441605581967e01c"
+                ],
+                "DnsEntries": [
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i-us-east-1d.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-04a4c8e2145cb966e-mt4iyi9i-us-east-1b.vpce-svc-0caf1c7342f032f18.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    }
+                ],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2019,
+                    "month": 10,
+                    "day": 20,
+                    "hour": 10,
+                    "minute": 53,
+                    "second": 39,
+                    "microsecond": 0
+                },
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "c7n"
+                    }
+                ],
+                "OwnerId": "644160558196"
+            },
+            {
+                "VpcEndpointId": "vpce-0bc8bcfcc74fa2db9",
+                "VpcEndpointType": "Gateway",
+                "VpcId": "vpc-072f438c953672ace",
+                "ServiceName": "com.amazonaws.us-east-1.s3",
+                "State": "available",
+                "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
+                "RouteTableIds": [
+                    "rtb-0b0e2aef9dd3d7580",
+                    "rtb-09e9ec4e3854addd7"
+                ],
+                "SubnetIds": [],
+                "Groups": [],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": false,
+                "NetworkInterfaceIds": [],
+                "DnsEntries": [],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 4,
+                    "day": 28,
+                    "hour": 20,
+                    "minute": 52,
+                    "second": 11,
+                    "microsecond": 0
+                },
+                "Tags": [],
+                "OwnerId": "644160558196"
+            },
+            {
+                "VpcEndpointId": "vpce-08ff0494faf0210d3",
+                "VpcEndpointType": "Interface",
+                "VpcId": "vpc-072f438c953672ace",
+                "ServiceName": "com.amazonaws.us-east-1.athena",
+                "State": "available",
+                "PolicyDocument": "{\n    \"Statement\": [\n        {\n            \"Action\": \"*\",\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\",\n            \"Principal\": \"*\"\n        }\n    ]\n}",
+                "RouteTableIds": [],
+                "SubnetIds": [
+                    "subnet-068dfbf3f275a6ae8",
+                    "subnet-03ec1a55eadb55221"
+                ],
+                "Groups": [
+                    {
+                        "GroupId": "sg-0d30141b566cfa039",
+                        "GroupName": "default"
+                    }
+                ],
+                "PrivateDnsEnabled": false,
+                "RequesterManaged": false,
+                "NetworkInterfaceIds": [
+                    "eni-01704d489bb6aeb98",
+                    "eni-029615f8fb7a39561"
+                ],
+                "DnsEntries": [
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by-us-east-1a.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    },
+                    {
+                        "DnsName": "vpce-08ff0494faf0210d3-idd6w0by-us-east-1b.athena.us-east-1.vpce.amazonaws.com",
+                        "HostedZoneId": "Z7HUB22UULQXV"
+                    }
+                ],
+                "CreationTimestamp": {
+                    "__class__": "datetime",
+                    "year": 2020,
+                    "month": 7,
+                    "day": 7,
+                    "hour": 19,
+                    "minute": 25,
+                    "second": 38,
+                    "microsecond": 0
+                },
+                "Tags": [],
+                "OwnerId": "644160558196"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcs_1.json b/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcs_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_vpc_endpoint_filter/ec2.DescribeVpcs_1.json
@@ -0,0 +1,160 @@
+{
+    "status_code": 200,
+    "data": {
+        "Vpcs": [
+            {
+                "CidrBlock": "10.0.0.0/24",
+                "DhcpOptionsId": "dopt-24ff1940",
+                "State": "available",
+                "VpcId": "vpc-0a59f94e3fca9fbc0",
+                "OwnerId": "644160558196",
+                "InstanceTenancy": "default",
+                "CidrBlockAssociationSet": [
+                    {
+                        "AssociationId": "vpc-cidr-assoc-07bd86efac3732a5b",
+                        "CidrBlock": "10.0.0.0/24",
+                        "CidrBlockState": {
+                            "State": "associated"
+                        }
+                    }
+                ],
+                "IsDefault": false,
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "implied"
+                    }
+                ]
+            },
+            {
+                "CidrBlock": "10.205.0.0/18",
+                "DhcpOptionsId": "dopt-24ff1940",
+                "State": "available",
+                "VpcId": "vpc-03005fb9b8740263d",
+                "OwnerId": "644160558196",
+                "InstanceTenancy": "default",
+                "CidrBlockAssociationSet": [
+                    {
+                        "AssociationId": "vpc-cidr-assoc-0f6765530d80dc044",
+                        "CidrBlock": "10.205.0.0/18",
+                        "CidrBlockState": {
+                            "State": "associated"
+                        }
+                    }
+                ],
+                "IsDefault": false,
+                "Tags": [
+                    {
+                        "Key": "Owner",
+                        "Value": "c7n"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "testing-vpc"
+                    }
+                ]
+            },
+            {
+                "CidrBlock": "192.168.0.0/16",
+                "DhcpOptionsId": "dopt-24ff1940",
+                "State": "available",
+                "VpcId": "vpc-01a1ffc078406ab62",
+                "OwnerId": "644160558196",
+                "InstanceTenancy": "default",
+                "CidrBlockAssociationSet": [
+                    {
+                        "AssociationId": "vpc-cidr-assoc-08a949c48a6dbd293",
+                        "CidrBlock": "192.168.0.0/16",
+                        "CidrBlockState": {
+                            "State": "associated"
+                        }
+                    }
+                ],
+                "IsDefault": false,
+                "Tags": [
+                    {
+                        "Key": "aws:cloudformation:stack-id",
+                        "Value": "arn:aws:cloudformation:us-east-1:644160558196:stack/eksctl-sanjay-cluster/ad933be0-88bf-11ea-8212-1208f0f76cbf"
+                    },
+                    {
+                        "Key": "DontTurnOff",
+                        "Value": "CloudCustodian"
+                    },
+                    {
+                        "Key": "Name",
+                        "Value": "eksctl-sanjay-cluster/VPC"
+                    },
+                    {
+                        "Key": "aws:cloudformation:logical-id",
+                        "Value": "VPC"
+                    },
+                    {
+                        "Key": "alpha.eksctl.io/eksctl-version",
+                        "Value": "0.18.0"
+                    },
+                    {
+                        "Key": "aws:cloudformation:stack-name",
+                        "Value": "eksctl-sanjay-cluster"
+                    },
+                    {
+                        "Key": "alpha.eksctl.io/cluster-name",
+                        "Value": "sanjay"
+                    },
+                    {
+                        "Key": "eksctl.cluster.k8s.io/v1alpha1/cluster-name",
+                        "Value": "sanjay"
+                    }
+                ]
+            },
+            {
+                "CidrBlock": "172.31.0.0/16",
+                "DhcpOptionsId": "dopt-24ff1940",
+                "State": "available",
+                "VpcId": "vpc-d2d616b5",
+                "OwnerId": "644160558196",
+                "InstanceTenancy": "default",
+                "CidrBlockAssociationSet": [
+                    {
+                        "AssociationId": "vpc-cidr-assoc-33669f5a",
+                        "CidrBlock": "172.31.0.0/16",
+                        "CidrBlockState": {
+                            "State": "associated"
+                        }
+                    }
+                ],
+                "IsDefault": true,
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "FlowLogTest"
+                    }
+                ]
+            },
+            {
+                "CidrBlock": "10.0.0.0/16",
+                "DhcpOptionsId": "dopt-24ff1940",
+                "State": "available",
+                "VpcId": "vpc-072f438c953672ace",
+                "OwnerId": "644160558196",
+                "InstanceTenancy": "default",
+                "CidrBlockAssociationSet": [
+                    {
+                        "AssociationId": "vpc-cidr-assoc-0194dd85e1c73db14",
+                        "CidrBlock": "10.0.0.0/16",
+                        "CidrBlockState": {
+                            "State": "associated"
+                        }
+                    }
+                ],
+                "IsDefault": false,
+                "Tags": [
+                    {
+                        "Key": "Name",
+                        "Value": "public with private"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_vpc.py b/tests/test_vpc.py
--- a/tests/test_vpc.py
+++ b/tests/test_vpc.py
@@ -258,6 +258,46 @@ def test_dhcp_options_filter(self):
         self.assertEqual([len(resources), resources[0]["VpcId"]], [1, "vpc-7af45101"])
         self.assertTrue("c7n:DhcpConfiguration" in resources[0])
 
+    def test_vpc_endpoint_filter(self):
+        factory = self.replay_flight_data("test_vpc_endpoint_filter")
+        p = self.load_policy(
+            {
+                "name": "vpc-endpoint-filter",
+                "resource": "vpc",
+                "filters": [
+                    {
+                        "type": "vpc-endpoint",
+                        "key": "ServiceName",
+                        "value": "com.amazonaws.us-east-1.s3",
+                    }
+                ],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertTrue("vpc-072f438c953672ace" in resources[0]["c7n:matched-vpc-endpoint"])
+
+    def test_subnet_endpoint_filter(self):
+        factory = self.replay_flight_data("test_subnet_endpoint_filter")
+        p = self.load_policy(
+            {
+                "name": "subnet-endpoint-filter",
+                "resource": "subnet",
+                "filters": [
+                    {
+                        "type": "vpc-endpoint",
+                        "key": "ServiceName",
+                        "value": "com.amazonaws.us-east-1.athena",
+                    }
+                ],
+            },
+            session_factory=factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 2)
+        self.assertTrue("subnet-068dfbf3f275a6ae8" in resources[0]["c7n:matched-vpc-endpoint"])
+
 
 class NetworkLocationTest(BaseTest):