The docs are very clear in doing this in the console, https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html, but replicating in CDK is really painful.
My question is how to create an Rest API backed with s3 (no lambda in the middle) in CDK, or at least how the apigateway.AwsIntegration works.
I've tried many things and realized that I was coding blind. I have made already multiple integrations between restApi, lambda, sqs, dynamoDB, s3 and all of them were so easy. But integrating API Gateway with S3 directly is about to make me cry.
I need that a restAPI store the request payload directly to an S3 bucket.
This is what I already tried:
Add a policy stament to RestApi:
const apiResourcePolicy = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:Put*'],
resources: [bucket.bucketName],
principals: [new ServicePrincipal('apigateway.amazonaws.com')]
})
]
});
const api = new apigateway.RestApi(this, "dispatch-api", {
restApiName: "my api name",
description: "api description",
policy: apiResourcePolicy
});
I thought that this should be enough to solve permissions issues, but when I added the AWS Integration to the API, like this:
const getS3Integration = new apigateway.AwsIntegration({
service: "s3",
path: bucket.bucketName,
});
api.root.addMethod("PUT", getS3Integration, {
requestValidator: requestValidator,
requestModels: {"application/json": myModel},
});
I'm getting:
1:41:11 PM | CREATE_FAILED | AWS::ApiGateway::Method | XXXXXXXXXXXXXXXX
Role ARN must be specified for AWS integrations (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: xxxxxxxxxxxxxxxxxxxxx; Proxy:
null)
I don't know how to specify that Role ARN, I did not found that in the docs. Don't know either if add policy to restAPI was necessary. I was trying to replicate the example here in CDK.
API Gateway sets the s3-host-name and passes the client specified bucket and key from the client to Amazon S3. (Optional) In Path override type /. Copy the previously created IAM role's ARN (from the IAM console) and paste it into Execution role. Leave any other settings as default.
You must set up necessary data mappings from the method request to the integration request, and from the integration response to the method response. HTTP_PROXY : The HTTP proxy integration allows a client to access the backend HTTP endpoints with a streamlined integration setup on single API method.
In the document you linked (https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html), section "Set up IAM permissions for the API to invoke Amazon S3 actions", the policy you need to use is totally different from what you encoded.
Your policy, if applied to a bucket, would say: if the request is from the service "api gateway", allow put:*
actions. This approach is for other use cases.
According to the documentation, you need:
put:*
in your case)apigateway.RestApi
The policy (1) should be obvious, the (2) as given in the documentation is:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Following is not the full set, but this should give you some ideas. I did similar things on SQS, I cried too back then
const bucket = new s3.Bucket(this, 'storage');
const executeRole = new iam.Role(this, "role", {
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
path: "/service-role/"
});
bucket.grantReadWrite(executeRole);
const api = new apigateway.RestApi(this, 's3api');
const s3Integration = new apigateway.AwsIntegration({
service: 's3',
integrationHttpMethod: "PUT",
path: "{bucket}",
options : {
credentialsRole: executeRole,
// should have all kind of path mapping..
}
})
api.root.addResource("{folder}").addMethod("PUT", s3Integration, {
methodResponses: [
{
statusCode: "200"
}
]});
}
From your description, it seems to me that you are not providing IAM role for your integration. You are providing a policy for RestApi
only which is a resource-based policy for your API. In other words, it does not apply to your integrations, but rather specifies who can execute your API. Generally you don't use it if you want to make your API public.
For AWS integration, you should be providing IAM role with S3 permissions using IntegrationOptions. Specifically, through credentials
option:
An IAM role that API Gateway assumes.
The CloudFormation docs, which CDK maps into, have more details on integration Credentials
:
The credentials that are required for the integration. To specify an AWS Identity and Access Management (IAM) role that API Gateway assumes, specify the role's Amazon Resource Name (ARN). To require that the caller's identity be passed through from the request, specify arn:aws:iam:::user/.
To use resource-based permissions on the AWS Lambda (Lambda) function, don't specify this property. Use the AWS::Lambda::Permission resource to permit API Gateway to call the function. For more information, see Allow Amazon API Gateway to Invoke a Lambda Function in the AWS Lambda Developer Guide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With