In a previous post, I explained how to connect AWS Step Functions to a private API Gateway endpoint thanks to the new integration with AWS PrivateLink and Amazon VPC Lattice. In this issue, I’ll show you how to use the same integration to use a private API Gateway API as an EventBridge target using the CDK, removing the need for an intermediary Lambda function.
Overview
The setup is similar to the one for Step Functions. A Resource Gateway is used as the entry point into the VPC. It is associated with a Resource Configuration, which defines the Private API Gateway resource, and the EventBridge Connection is configured to use the Resource Config as the final destination.
For more details about this setup, see my previous post about the Step Functions Integration.
CDK Stack Definition
We need to define the Resource Gateway and the Resource Definition.
// Security Group for the Resource Gateway
const rgSecurityGroup = new SecurityGroup(this, 'ResourceGatewaySG', {
vpc: vpc,
allowAllOutbound: false,
});
rgSecurityGroup.addEgressRule(
Peer.ipv4(vpc.vpcCidrBlock),
Port.tcp(443),
'Allow HTTPS traffic from Resource Gateway',
);
// Resource Gateway
const resourceGateway = new CfnResourceGateway(this, 'ResourceGateway', {
name: 'private-api-access',
ipAddressType: 'IPV4',
vpcIdentifier: vpc.vpcId,
subnetIds: vpc.isolatedSubnets.map((subnet) => subnet.subnetId),
securityGroupIds: [rgSecurityGroup.securityGroupId],
});
// Resource Configuration
const resourceConfig = new CfnResourceConfiguration(
this,
'ResourceConfig',
{
name: 'sf-private-api',
portRanges: ['443'],
resourceGatewayId: resourceGateway.ref,
resourceConfigurationType: 'SINGLE',
},
);
// Use the global DNS name of the API gateway's VPC endpoint
// in the Resource Configuration
resourceConfig.addPropertyOverride(
'ResourceConfigurationDefinition.DnsResource',
{
DomainName: Fn.select(
1,
Fn.split(':', Fn.select(0, api.vpcEndpoint.vpcEndpointDnsEntries)),
),
IpAddressType: 'IPV4',
},
);
// Event Bus
const eventBus = new EventBus(this, 'EventBus', {});
// Connection to the API
const connection = new Connection(this, 'ApiConnection', {
authorization: Authorization.apiKey(
'x-api-key',
SecretValue.unsafePlainText('demo'),
),
});
// Setup the Connection with the Resouce Config
(connection.node.children[0] as CfnConnection).addPropertyOverride(
'InvocationConnectivityParameters',
{
ResourceParameters: {
ResourceConfigurationArn: resourceConfig.attrArn,
},
},
);
EventBridge is now able to connect to the private API Gateway. We can now create a rule and set the API as the target.
const rule = new Rule(this, 'RequestAccountRule', {
eventBus,
eventPattern: {
source: ['my-source'],
},
});
const apiDestination = new ApiDestination(this, 'ApiDestination', {
endpoint: `${api.api.url}/hello`,
httpMethod: HttpMethod.POST,
connection: connection,
});
rule.addTarget(
new targets.ApiDestination(apiDestination, {
event: RuleTargetInput.fromEventPath('$.detail'),
}),
);
Find the full code on GitHub.
Testing the Integration
Putting the following event on the bus.
{
"DetailType": "somethingHappened",
"Source": "my-source",
"EventBusName":"EventBusVendingMachine308DEFEB",
"Detail": {
"foo": "bar"
}
}
I can see that the Lambda function used as the handler of the endpoint is invoked with the following event.
{
"resource": "/hello",
"path": "/hello",
"httpMethod": "POST",
"headers": {
"Accept-Encoding": "gzip, x-gzip, deflate, br",
"Content-Type": "application/json; charset=utf-8",
"Host": "899aggxh3a.execute-api.us-east-1.amazonaws.com",
"Range": "bytes=0-1048575",
"User-Agent": "Amazon/EventBridge/ApiDestinations",
"x-amzn-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
"x-amzn-tls-version": "TLSv1.2",
"x-amzn-vpc-id": "vpc-0a1db1c1701e137ca",
"x-amzn-vpce-config": "1",
"x-amzn-vpce-id": "vpce-09fc3c0c5173d919b",
"x-api-key": "demo",
"X-Forwarded-For": "10.0.195.243"
},
"multiValueHeaders": {
"Accept-Encoding": [
"gzip, x-gzip, deflate, br"
],
"Content-Type": [
"application/json; charset=utf-8"
],
"Host": [
"899aggxh3a.execute-api.us-east-1.amazonaws.com"
],
"Range": [
"bytes=0-1048575"
],
"User-Agent": [
"Amazon/EventBridge/ApiDestinations"
],
"x-amzn-cipher-suite": [
"ECDHE-RSA-AES128-GCM-SHA256"
],
"x-amzn-tls-version": [
"TLSv1.2"
],
"x-amzn-vpc-id": [
"vpc-0a1db1c1701e137ca"
],
"x-amzn-vpce-config": [
"1"
],
"x-amzn-vpce-id": [
"vpce-09fc3c0c5173d919b"
],
"x-api-key": [
"demo"
],
"X-Forwarded-For": [
"10.0.195.243"
]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "yjzggg",
"resourcePath": "/hello",
"httpMethod": "POST",
"extendedRequestId": "Efl1aFY5oAMFoYA=",
"requestTime": "16/Jan/2025:18:29:54 +0000",
"path": "/prod/hello",
"accountId": "438465158289",
"protocol": "HTTP/1.1",
"stage": "prod",
"domainPrefix": "899aggxh3a",
"requestTimeEpoch": 1737052194204,
"requestId": "3a6754ae-5488-429c-9ec6-1837a4c21727",
"identity": {
"cognitoIdentityPoolId": null,
"cognitoIdentityId": null,
"vpceId": "vpce-09fc3c0c5173d919b",
"apiKey": "demo",
"principalOrgId": null,
"cognitoAuthenticationType": null,
"userArn": null,
"userAgent": "Amazon/EventBridge/ApiDestinations",
"accountId": null,
"caller": null,
"sourceIp": "10.0.195.243",
"accessKey": null,
"vpcId": "vpc-0a1db1c1701e137ca",
"cognitoAuthenticationProvider": null,
"user": null
},
"domainName": "899aggxh3a.execute-api.us-east-1.amazonaws.com",
"deploymentId": "74v610",
"apiId": "899aggxh3a"
},
"body": "{\"foo\":\"bar\"}",
"isBase64Encoded": false
}
Conclusion
The new VPC Lattice and AWS Private Link integration allows developers to invoke Private APIs directly without needing a Lambda function. This reduces code, maintenance, and latency.
Top comments (0)