I connected the Lambda to EFS, and I want to download small file from S3 to the EFS using Lambda.
I connected the Lambda function to file system, and added access point with permissions 777.
I have this small python function in Lambda to download to the mount:
import json
import boto3
import time
import os
s3 = boto3.client('s3')
def lambda_handler(event, context):
s3.download_file('my_bucket', 'img.jpg', '/mnt/my-efs/img.jpg')
return{
"statusCode": 200
}
And I get timeout after 1 min (this should take less than a second).
If I download for /tmp/img.jpg
, this works. Even if I copy the file from /tmp/
to /mnt/my-efs/
this works.
I have this IAM for the Lambda user:
AmazonEC2FullAccess
AmazonS3FullAccess
AmazonVPCFullAccess
AWSLambdaVPCAccessExecutionRole
AmazonElasticFileSysstemClientReadWriteAccess
AmazonElasticFileSysstemClientFullAccess
Why can't I download file from S3 to EFS using Lambda?
This get's a big tricky so let's go slow and make sure we cover all the moving parts. I apologize if some of this is redundant, but I wanted to make sure a layman would have all the right parts. Please let me know if you think I've missed something.
Steps:
serverless.yaml
1. serverless.yaml
I'm using serverless so this is what my serverless.yaml
file looks like.
iamRoleStatements:
- Effect: Allow
Action:
- elasticfilesystem:ClientMount
- elasticfilesystem:ClientWrite
- elasticfilesystem:ClientRootAccess
Resource:
- arn:aws:elasticfilesystem:us-west-2:123456789:file-system/fs-123456789
These were the minimum permissions i needed to download the file from S3 into the EFS drive location. Again, this project uses serverless so all this can be translated to a cloudFormation template if you're not using serverless.
And under the handler function...
functions:
myFunc:
handler: handler.handle
package:
exclude:
- env/**
- node_modules/**
description: Lambda to download S3 file to SFTP EFS folder.
events:
- s3:
bucket: some-bucket
event: s3:ObjectCreated:*
rules:
- prefix: some-folder/
- suffix: .jpg
existing: true
fileSystemConfig:
localMountPath: /mnt/my-efs
arn: arn:aws:elasticfilesystem:us-west-2:123456789:access-point/fsap-12345678910
NOTE: I left out a lot from my serverless.yaml
file, and only included the parts pertaining to the EFS setup.
2. AWS EFS Access Point Settings
File System:
Choose a dropdown selectionName - optional
Pick a nameRoot directory path - optional:
This will map to your underlying file system on the EC2, or SFTP server. I'm using an sftp server, so I simply input the address on the sftp server. sftp.companyX.com/images
. This location will map to our Local Mount Path
set inside your Lambda settings within the EFS configuration window (we'll look more at that in step 3).User ID:
1054
| Locate your /etc/passwd/
file and verify this to be true for your server hosting the EFS drive. I think 1054
is the default setting for amazon linux but if you're running a different OS on your EFS host, you should verify this value.Group ID:
1054
(see last line)Owner User ID:
1054
(see last line)Owner Group ID:
1054
(see last line)POSIX permissions to apply to the root directory path
: 777
| understand these permissions translate to your iamRoleStatements.Action
values; if they're different this 777
should be set appropriately.3. Lambdas addition of the EFS
EFS File System:
(choose from dropdown selection)Local Mount Path:
| SUPER IMPORTANT e.g. /mnt/my-efs
. This part is very non-intuitive IMO. This location, whatever you want it to be, will map to the Root directory path
(step 2.4 above) you configured when you build your access point. Meaning, if you download an s3 file called img.jpg
to /mnt/my-efs/.
it will put that file into sftp.company.com/images/img.jpg
.4. Downloading items into the EFS drive
class DownloadService:
def __init__(self, *args, **kwargs):
self.s3 = kwargs.get('s3_service')
def download_files_to_efs(self, event):
"""
Locates the S3 file name (i.e. S3 object "key" value) the initiated the Lambda call, then downloads the file
into the locally attached EFS drive at the target location.
:param: event | S3 event record
:return: dict
"""
key = event \
.get('Records')[0] \
.get('s3') \
.get('object') \
.get('key') # 'some-bucket/some-folder/img.jpg'
efs_loci = f"/mnt/my-efs/images/{key.split('/')[-1]}" # '/mnt/my-efs/images/img.jpg'
result = self.s3.download('some-bucket', key, efs_loci)
if result:
print('Download Success...')
else:
print('Ask Stack Overflow :/')
return {'status_code': 200}
5. Verifying Your Files are in EFS
import os
class DownloadService:
#...constructor stuff
@staticmethod
def get_files_from_path(file_path):
"""
Returns the files found at the file_path
:param file_path: string
:return list:
"""
# NOTE: file_path = '/my-efs/
found_files = []
for _, _, f in os.walk(file_path):
found_files.append(f)
if found_files:
print('efs files: ', found_files)
return found_files
Special attention here! Notice i didn't read from /mnt/my-efs/
i read from /my-efs
, this is because the EFS /
directory maps to the /mnt
directory in the Lambda environment.
Finished 🚀
NOTE: If i've left something out feel free to comment and i'll add that as well if i can. Unfortunately i can't share the repo as it's a work related project (protected).
In addition of @Tobiah answer. I got this answer from aws :
To access S3 service from a Lambda function we need either internet access or a S3 VPC endpoint.
Now, inorder to give lambda function internet access from inside of VPC, we would need to route traffic through a NAT gateway or NAT Instance. Placing lambda inside Public Subnet of VPC will not give internet access to lambda. [1] You can attach private subnet(s) to lambda function which has a default route to the NAT gateway or NAT Instance which is launched inside the public subnet of VPC. Please refer to this document [2] for step by step guide. Kindly note that using NAT gateway will incur additional charges. [3] Alternatively to use S3 VPC endpoint to connect lambda to S3. Please refer this document [4] for step by step guide. Also, for encrypting files in EFS refer this document[5].
[1] https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html#vpc-internet
[2] https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/
[3] https://aws.amazon.com/vpc/pricing/
[4] https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html
[5] https://docs.aws.amazon.com/efs/latest/ug/encryption.html
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