Like most other things, solving said problem really is quite straight-forward
once you know how. My Googlings where unsuccessful in this area, so I'll
detail how here, in case someone out there has the same problem.
In the interest of providing a complete, working configuration, I'll include all
the required parts. If you already have an API configuration, you'll probably be
most interested in the AWS::ApiGateway::Method
resource.
At the top of the mountain is a Rest API:
Api:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: MyProxyAPI
In order to proxy all paths to the API, you need two resources: the root
resource, and a catch-all resource. Well, a catch-almost-all anyway, since the
catch-all does not catch the root resource. The root resource is created for
you, the proxy resource is not:
Resource:
Type: 'AWS::ApiGateway::Resource'
Properties:
ParentId: !GetAtt Api.RootResourceId
RestApiId: !Ref Api
PathPart: '{proxy+}'
As you can see, this resource references the Api.RootResourceId
as its parent.
The path part {proxy+}
is a greedy match for any path. If you wanted to only
match requests under e.g. /blog/*
, you'd have to define two resources:
BlogResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
ParentId: !GetAtt Api.RootResourceId
RestApiId: !Ref Api
PathPart: 'blog'
Resource:
Type: 'AWS::ApiGateway::Resource'
Properties:
ParentId: !Ref BlogResource
RestApiId: !Ref Api
PathPart: '{proxy+}'
Next up we'll configure the methods. As I want to proxy everything, I just
define one ANY
method for each resource.
The root resource is a 1:1 from the root path to the root path on your proxy
target. For this example, we're proxying to an imaginary S3 bucket website:
RootMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: ANY
ResourceId: !GetAtt Api.RootResourceId
RestApiId: !Ref Api
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://my-imaginary-bucket.s3-website-eu-west-1.amazonaws.com/
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
Next up is the proxy resource method, and this is what took me an embarrassing
amount of time to figure out.
ProxyMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: ANY
ResourceId: !Ref Resource
RestApiId: !Ref Api
AuthorizationType: NONE
RequestParameters:
method.request.path.proxy: true
Integration:
CacheKeyParameters:
- 'method.request.path.proxy'
RequestParameters:
integration.request.path.proxy: 'method.request.path.proxy'
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://my-imaginary-bucket.s3-website-eu-west-1.amazonaws.com/{proxy}
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
Let's discuss the key components of this. First of all, setting the resource
path to {proxy+}
is not enough to be able to use this in the target URL.
You also need to specify RequestParameters
to state that it is OK to use the
proxy
parameter from the path in the integration configuration.
As if that wasn't enough, you also have to inform Cloudformation of how you will
access the proxy
parameter in your integration request path, by specifying
Integration.RequestParameters
. It is a map of parameters from the method
request to parameters in the integration request.
Those two bits are crucial, because now we can finally use {proxy}
to insert
the proxied path in our integration uri.
In order to use the API you need a deployment. Because the deployment does not
have a direct dependency on either of the methods, and because we cannot deploy
an API with no methods, we use DependsOn
to help Cloudformation figure out the
order of things:
Deployment:
DependsOn:
- RootMethod
- ProxyMethod
Type: 'AWS::ApiGateway::Deployment'
Properties:
RestApiId: !Ref Api
StageName: dev
Choose a stage name of your liking.
That's all there is to it. Doesn't look very hard when you know what to do.
AWSTemplateFormatVersion: 2010-09-09
Description: An API that proxies requests to another HTTP endpoint
Resources:
Api:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: SomeProxyApi
Resource:
Type: 'AWS::ApiGateway::Resource'
Properties:
ParentId: !GetAtt Api.RootResourceId
RestApiId: !Ref Api
PathPart: '{proxy+}'
RootMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: ANY
ResourceId: !GetAtt Api.RootResourceId
RestApiId: !Ref Api
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://my-imaginary-bucket.s3-website-eu-west-1.amazonaws.com/
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
ProxyMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
HttpMethod: ANY
ResourceId: !Ref Resource
RestApiId: !Ref Api
AuthorizationType: NONE
RequestParameters:
method.request.path.proxy: true
Integration:
CacheKeyParameters:
- 'method.request.path.proxy'
RequestParameters:
integration.request.path.proxy: 'method.request.path.proxy'
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: http://my-imaginary-bucket.s3-website-eu-west-1.amazonaws.com/{proxy}
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
Deployment:
DependsOn:
- RootMethod
- ProxyMethod
Type: 'AWS::ApiGateway::Deployment'
Properties:
RestApiId: !Ref Api
StageName: !Ref StageName