
This assumes you have already configured the aws command line tool. If not, check out my article on Getting Started with the AWS CLI.
There are different type of routes that can be created with Amazon Web Services (AWS) API Gateway.
- HTTP route
- REST API route (this article)
At a high level, you will
- Create a backend service that the API Gateway will route requests onto (a Lambda Function in this example)
- Create the API Gateway REST API
- Update the API Gateway REST API with methods (e.g. GET, POST)
- Create a Security Group that allows HTTPS port 443
- Attach a Resource Policy to the REST API to only allow requests in the same Virtual Private Cloud (VPC) as the REST API
- Create a Virtual Private Cloud (VPC) execute-api Endpoint that uses a Security Group that allows incoming requests on HTTPS port 443
- Create or Update and IAM Role with a Trust Policy and Permission Policy that will allow the API Gateway to assume the Role
- Integrate the Lambda Function with the API Gateway
- Update the API Gateway Response to return 200 OK
- Optional - Setup API Gateway to append logs to CloudWatch
- Deploy the REST API
For this walkthrough, let's say you have a Lambda Function that returns "Hello from Lambda!".
Create API Gateway REST API
The aws apigateway create-rest-api command can be used to create the API Gateway REST API. By default, the endpoint will be EDGE. The --endpoint-configuration option can be used to set the REST API endpoint type to private.
~]$ aws apigateway create-rest-api --name my-rest-api --endpoint-configuration='{"types":["PRIVATE"]}'
{
"apiKeySource": "HEADER",
"name": "my-rest-api",
"createdDate": 1704766329,
"endpointConfiguration": {
"types": [
"PRIVATE"
]
},
"id": "o03n2c8eha"
}
At this point, if you were to look at the API Gateway in the AWS console, you would see the API Gateway has no methods, such as GET or POST.
Update the API Gateway REST API with methods (e.g. GET, POST)
The aws apigateway get-resources command can be used to see that the API Gateway has no methods.
~]$ aws apigateway get-resources --rest-api-id o03n2c8eha
{
"items": [
{
"id": "6gv3n2wtze",
"path": "/",
"resourceMethods": {}
}
]
}
The aws apigateway put-method command can be used to update the API Gateway REST API with the methods it will allow, such as GET.
aws apigateway put-method \
--rest-api-id o03n2c8eha \
--resource-id 6gv3n2wtze \
--http-method GET \
--authorization-type "NONE" \
--no-api-key-required
And POST.
aws apigateway put-method \
--rest-api-id o03n2c8eha \
--resource-id 6gv3n2wtze \
--http-method POST \
--authorization-type "NONE" \
--no-api-key-required
Now the aws apigateway get-resources command should show that the API Gateway has methods.
~]$ aws apigateway get-resources --rest-api-id 2885zecrwj
{
"items": [
{
"id": "sf6n2hrix7",
"path": "/",
"resourceMethods": {
"GET": {},
"POST": {}
}
}
]
}
Likewise, in the AWS console, the API Gateway should have the methods.
Create a Security Group that allows HTTPS port 443
The aws ec2 describe-vpcs command can be used to list your Virtual Private Clouds (VPCs).
~]$ aws ec2 describe-vpcs
{
"Vpcs": [
{
"CidrBlock": "10.0.0.0/24",
"DhcpOptionsId": "dopt-017f0a715e4ce2fc9",
"State": "available",
"VpcId": "vpc-0a9d4cb29e2748444",
"OwnerId": "123456789012",
"InstanceTenancy": "default",
"CidrBlockAssociationSet": [
{
"AssociationId": "vpc-cidr-assoc-07bcabb27322c8281",
"CidrBlock": "10.0.0.0/24",
"CidrBlockState": {
"State": "associated"
}
},
{
"AssociationId": "vpc-cidr-assoc-0113cedd8171ec855",
"CidrBlock": "10.31.0.0/16",
"CidrBlockState": {
"State": "associated"
}
}
],
"IsDefault": false,
"Tags": [
{
"Key": "Name",
"Value": "my-vpc"
}
]
}
]
]
Almost always, requests will be submitted to the REST API over HTTPS port 443. The aws ec2 create-security-group command can be used to create a Security Group in your VPC.
~]$ aws ec2 create-security-group --vpc-id vpc-0a9d4cb29e2748444 --group-name apiGatewayRestApi --description apiGatewayRestApi --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=apiGatewayRestApi}]' {
"GroupId": "sg-083870552fd33fe48",
"Tags": [
{
"Key": "Name",
"Value": "apiGatewayRestApi"
}
]
}
And then the aws ec2 authorize-security-group-ingress command can be used to allow incoming requests on HTTPS port 443.
aws ec2 authorize-security-group-ingress --group-id sg-083870552fd33fe48 --protocol tcp --port 443 --cidr 0.0.0.0/0
Virtual Private Cloud endpoint
The aws ec2 describe-subnets command can be used to get the subnet IDs in your VPC.
~]$ aws ec2 describe-subnets --filter "Name=vpc-id,Values=vpc-0a9d4cb29e2748444" | grep "SubnetId"
"SubnetId": "subnet-0f015da3a1e164304",
"SubnetId": "subnet-0d2d8580c46d6d280",
"SubnetId": "subnet-02b9845e7366bdf89",
"SubnetId": "subnet-075d4be5a8a07c818",
The aws ec2 create-vpc-endpoint command can be used to create a Virtual Private Cloud endpoint that will allow the execute-api service in the Virtual Private Cloud Subnets, allowing incoming requests listed in the Security Group you just created.
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0a9d4cb29e2748444 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.us-east-1.execute-api \
--subnet-ids subnet-0f015da3a1e164304 subnet-0d2d8580c46d6d280 subnet-02b9845e7366bdf89 subnet-075d4be5a8a07c818 \
--security-group-id sg-083870552fd33fe48 \
--tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=service,Value=execute-api}]'
Attach a Resource Policy to the REST API
Create a Resource Policy to only allow requests to be submitted to the REST API that originate in Virtual Private Cloud (VPC) vpc-123456789012.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
]
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
],
"Condition" : {
"StringNotEquals": {
"aws:SourceVpc": "vpc-0a9d4cb29e2748444"
}
}
}
]
}
IAM Role with Trust Policy and Permission Policy
Next we will be updating the API Gateway to be integrated with our Lambda Function. But we will first need an IAM Role that will allow the API Gateway service to assume the IAM Role and the Role will need to allow Lambda actions. Let's create a file with the following JSON. It can be named anything, such as my.json. Notice this will allow the apigateway.amazonaws.com service to assume the role. This is a Trust Policy. It's basically a way of saying "I trust you" or "I trust the apigateway.amazonaws.com service.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "apigateway.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
And then the aws iam create-role command can be used to create the role.
aws iam create-role --role-name my-role --assume-role-policy-document file://my.json
And then the aws iam attach-role-policy can be used to attach the AWSLambda_FullAccess Permission Policy to the role.
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AWSLambda_FullAccess --role-name my-role
CloudWatch
Optionally, you can update the API Gateway to log events to CloudWatch. Check out my article Setup API Gateway to append logs to CloudWatch.
Update API Gateway Response
Let's add a response for 200 OK.
aws apigateway update-method-response \
--rest-api-id 2885zecrwj \
--resource-id sf6n2hrix7 \
--http-method POST \
--status-code 200
At this point, if you were to try to submit a request to the REST API from an EC2 instance in the same Virtual Private Cloud (VPC) as the API Gateway REST API, you should get something like "Could not resolve host".
~]$ curl --request POST --url https://2885zecrwj.execute-api.us-east-1.amazonaws.com/
curl: (6) Could not resolve host: 2885zecrwj.execute-api.us-east-1.amazonaws.com
Beause the REST API needs to be deployed for it to be available. By default, creating the REST API does not deploy the REST API as can be seen by the fact that this command returns an empty list (no deployments).
~]$ aws apigateway get-deployments --rest-api-id 2885zecrwj
{
"items": []
}
Let's create the first deployment.
aws apigateway create-deployment \
--rest-api-id 2885zecrwj \
--stage-name dev \
--stage-description "Development Stage" \
--description "First Deployment"
You should now be able to go to use the Test tab in the AWS API Gateway console and get a response from the Lambda Function.
Likewise, you should be able to submit a POST requests to the REST API from a system in your Virtual Private Cloud, such as an EC2 instance, and if all the pieces are in place here, your Lambda Function should return output. In this example, the endpoint is /dev since the --stage-name of the deployment was dev.
curl \
--request POST \
--header 'Content-Type: application/json' \
--user <access key>:<secret key> \
--url https://2885zecrwj.execute-api.us-east-1.amazonaws.com/dev
Whatever response your Lambda function is set to return should be returned. Hooray - it works!
{"statusCode": 200, "body": "\"Hello from Lambda!\""}
But if you do the same from a system that is not in your Virtual Private Cloud, you should get something like this, which shows this API Gateway is now PRIVATE, it can only be used from within your VPC. Perfect!
curl: (6) Could not resolve host: 2885zecrwj.execute-api.us-east-1.amazonaws.com
Did you find this article helpful?
If so, consider buying me a coffee over at