Skip to content

Instantly share code, notes, and snippets.

@thejasonfisher
Forked from plindberg/README.md
Created July 21, 2021 01:32
Show Gist options
  • Save thejasonfisher/22a7f43ddc4fabae70e3d09b69b800e3 to your computer and use it in GitHub Desktop.
Save thejasonfisher/22a7f43ddc4fabae70e3d09b69b800e3 to your computer and use it in GitHub Desktop.
How to set up an AWS Lambda function for returning S3 pre-signed URLs for uploading files.

README

Granted, this is little more than an obfuscated way of having a publicly writable S3 bucket, but if you don’t have a server which can pre-sign URLs for you, this might be an acceptable solution.

For this to work, you take the following steps:

  1. Create a Lambda func, along with a new IAM role, keeping the default code.
  2. Create an API in the API Gateway.
  3. Create a resource in said API.
  4. Create a POST method for that API resource, pointing it to the above Lambda func.
  5. Deploy the API to a new stage.
  6. Verify that you can call the Lambda func using curl followed by the URL shown for the stage, resource, and method.
  7. Create an S3 bucket if you haven’t already, and one or more folders you want to upload to.
  8. Add a bucket policy granting public read access to those folders (a sample is included in this gist).
  9. Create an IAM policy granting write permissions in those folders (also included).
  10. Attach this policy to the IAM role created above.
  11. Update the Lambda func with the code in this gist.
  12. Add a configuration variable named s3_bucket with the name of your S3 bucket.
  13. Verify that you can call it using curl followed by the same URL as previously, followed by the parameters --request POST --header 'Content-Type: application/json --data '{"object_key": "folder/filename.ext"}'
  14. Finally, verify that you can upload a file to the URL returned, using curl --upload-file followed by the path to some file and the URL received in the previous step.

That’s it!

'use strict';
const AWS = require('aws-sdk');
const s3 = new AWS.S3({signatureVersion: 'v4'});
exports.handler = (event, context, callback) => {
const bucket = process.env['s3_bucket'];
if (!bucket) {
callback(new Error(`S3 bucket not set`));
}
const key = event['object_key'];
if (!key) {
callback(new Error('S3 object key missing'));
return;
}
const params = {'Bucket': bucket, 'Key': key};
s3.getSignedUrl('putObject', params, (error, url) => {
if (error) {
callback(error);
} else {
callback(null, {url: url});
}
});
};
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::mybucket/folder1/*"
]
},
{
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::mybucket/folder2/*"
]
}
]
}
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mybucket/folder1/*"
},
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mybucket/folder2/*"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment