# Invoking Private API Gateway Endpoints From Step Functions

At Re:Invent 2024, AWS announced [EventBridge and Step Functions integration with private APIs](https://aws.amazon.com/about-aws/whats-new/2024/12/amazon-eventbridge-step-functions-integration-private-apis/). Thanks to this new feature, customers can now directly invoke APIs that are inside a private VPC from EventBridge (with [API destinations](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html)), or Step Functions ([HTTP Tasks](https://docs.aws.amazon.com/step-functions/latest/dg/call-https-apis.html)). Before, users had to use Lambda functions inside the VPC as a proxy to their private APIs.

In a previous post, I explained how to [invoke HTTP endpoints from Step Functions with the CDK](https://benoitboure.com/calling-external-endpoints-with-step-functions-and-the-cdk). In this issue, I will cover calling a private API Gateway endpoint using the new integration.

## Overview

First, let’s examine how this new integration works and how the different components interact with each other. At the end of this post, I’ll show you how to deploy this setup with the CDK.

Here is a diagram that describes the architecture.

![PlantUML diagram](https://cdn-0.plantuml.com/plantuml/png/dL7DRXCn4BxFKxWveAt4nd13FKGjePHA5Og816TdFRknyDgMFSwgGhmxirddXq8ES4WyCzzFlfdS9bAHSc_XIcDh78gxR-iLzs9B5DADb54DyyxGDczomjXuH-XetlXUgY5PjKdZMni6KjtwM0Uht6WeTs_VpTz8RH81N1dNsAoFxfBVfUzxx-Q1sx_YQzC7Qrg3-WBd8VeSalowMbuWy2-4J2YVLB_HwWBfCzBWutVZkkMqsmUqPeVnUJI-TpfuuoXTYXauOgF8UFV8uYxkItctUdnGX8Dw_ZVTcZ1ypAuPc_G_UPyKaMbmaWByvbUDijRwuRMOZO0u8ZEUpAu1s61_qyhXm3LF-NjsBNu0275oho8cdsE3PSU99meglXHK5BYu2t5-pseNcaDJz8Vso3zTiLB1y9G7VvXE_ssrLKvRZ3pzD5M5y1FWi7QzU97xvdw7Zjv7epiKV4o7-tE8LwSLUDgQ3bu8wyLPUZYhwmK71VxKYn8806xHwTpRNm00 align="left")

[Source](https://www.plantuml.com/plantuml/uml/dL71ZjCm4BtxAxmzeAn4QhYXFLIxb6LPQOKgAi7PZIVf2CUsx76Z5UBVcJHfA8KSs4FYcJTlNfvVRXFfIBcruif0ZGxatRVjXdkv9mhfHgceksM3jC-xd21MtX4uMbQ-LRfBLkzIVvR8WrJMFfR1QjSBgiFRTyitoc0Y8QxGLJQRILtnkVPjwzqoSFlF-HRROB56C3ESX-XpIEhhPZr3u2-4JA2UTBipUeRq6QZpyJkwPZtSxGDOF41yxeNldGaU7QKvcu4jLfhGkqTURkAnL7URnmTDqEdd_zlR4eIFsLLzarxYzqaJOGN3gX1_w1NzMcrzzrek-e6S9Wj65jT2iC0nqy91npMZ_5vSonz2olCmYaEeJir0agTsb6B-PAQ8a7oE5OoHCEFBYCWHchP-1rVeW8moy1Tf-9t5NZjZ8JBwQQX6mayXJZSj8pPxAbSN3cxa_G4SlOze6f0SeuDZ4FALd9mnMcCZBZRBrTdLnLbThjYluATSZRw4k0LdScj_0G00)

The new private API integration is powered by [VPC Lattice](https://aws.amazon.com/vpc/lattice/) and [AWS PrivateLink](https://aws.amazon.com/privatelink/). VPC Lattice has two new features that make connecting Step Functions to a VPC possible: *Resource Gateways* and *Resource Configurations*

### **Resource Gateway**

A [resource gateway](https://docs.aws.amazon.com/vpc/latest/privatelink/resource-gateway.html) is a point of entry into the VPC where your resources reside. It can span one or more availability zones through the VPC subnets.

To access a private API Gateway from Step Functions, we need a Resource Gateway that lives in the same VPC and subnets as the VPC endpoint that is attached to the API.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1736791560083/19e3bba4-c459-4a59-87d9-2ca87d48aa28.png align="center")

### Resource Configuration

Once we have a Resource Gateway for our VPC, we can create and attach [Resource Configurations](https://docs.aws.amazon.com/vpc-lattice/latest/ug/resource-configuration.html) to it. Resource Configurations represent resources that are accessible through the gateway, and how they are accessed.

In the case of API Gateway, a resource configuration consists of the VPC endpoint’s regional DNS name. We can also specify a port or range of ports that are accessible, which in our case is just `443` (for HTTPS).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1736791485500/abe42ff7-f635-4182-bfe1-7f32afe14617.png align="center")

### EventBridge Connection

To call HTTP endpoints, Step Functions uses [EventBridge Connections](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-target-connection.html). The connection defines the authorization method, and the credentials to access the endpoint. Connections now have a new capability that allows integration with private APIs through a VPC Lattice Resource Configuration.

For API Gateway, the Resource Configuration is the one that defines the API Gateway.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1736791696972/19cb1b19-3442-4ab4-8aa0-9d3112e6cdcc.png align="center")

And that’s all we need. Everything else works the same as [calling a public HTTP endpoint](https://benoitboure.com/calling-external-endpoints-with-step-functions-and-the-cdk).

## Definition With the CDK

As explained earlier, we need a Resource Gateway that serves as the point of ingress into our VPC. We also create a security group that only allows egress to port 443, which is all we need for this use case:

```typescript
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), // all isolated subnets
  securityGroupIds: [rgSecurityGroup.securityGroupId],
});
```

We also need a Resource Config that describes the API Gateway’s VPC endpoint.

```typescript
// Resource Configuration
const resourceConfig = new CfnResourceConfiguration(
  this,
  'ResourceConfig',
  {
    name: 'sf-private-api',
    portRanges: ['443'],
    resourceGatewayId: resourceGateway.ref,
    resourceConfigurationType: 'SINGLE',
  },
);

resourceConfig.addPropertyOverride(
  'ResourceConfigurationDefinition.DnsResource',
  {
    DomainName: Fn.select(
      1,
      Fn.split(':', Fn.select(0, api.vpcEndpoint.vpcEndpointDnsEntries)),
    ),
    IpAddressType: 'IPV4',
  },
);
```

At the time of writing, the `CfnResourceConfiguration` L1 construct does not support `DnsResource` for `ResourceConfigurationDefinition`, so I’m using an override. For `DomainName`, we need the regional public DNS name of the [VPC endpoint of the API Gateway](https://github.com/bboure/cdk-step-functions-private-api-gateway/blob/main/lib/constructs/PrivateApi.ts#L38-L45), which is the first item of the [DnsEntries](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpcendpoint.html#aws-resource-ec2-vpcendpoint-return-values) CloudFormation returned value. It’s prefixed with the hosted zone id, so I’m using intrinsic functions to extract the value.

We can now use the configuration in our Step Functions definition:

```typescript
const connection = new Connection(this, 'ApiConnection', {
  authorization: Authorization.apiKey(
    'x-api-key',
    SecretValue.unsafePlainText('demo'),
  ),
});

(connection.node.children[0] as CfnConnection).addPropertyOverride(
  'InvocationConnectivityParameters',
  {
    ResourceParameters: {
      ResourceConfigurationArn: resourceConfig.attrArn,
    },
  },
);

const http = new HttpInvoke(this, 'Http', {
  apiRoot: api.url, // url of the API Gateway
  apiEndpoint: TaskInput.fromText(`hello`),
  method: TaskInput.fromText('GET'),
  connection: connection,
});
```

The `Connection` construct does not support `InvocationConnectivityParameters` yet, so I’m also using an override here as well.

And here you have it! You can find this code in full on [GitHub](https://github.com/bboure/cdk-step-functions-private-api-gateway/blob/main/lib/step-functions-private-api-stack.ts).

## Conclusion

With this new integration with Amazon VPC Lattice and AWS PrivateLink, teams now have the ability to invoke private API Gateway endpoints directly from AWS Step Functions or Amazon EventBridge. This eliminates the need for a Lambda Function, which in turn reduces the amount of code required and decreases overhead. It's a significant step forward for teams looking to optimize and simplify their cloud infrastructure.
