In this article, we will demonstrate how to access an EFS file system in one account from a Lambda function in another account, with both the EFS and the Lambda function residing within their own VPCs.
Overview
For the Lambda function to connect to an EFS across accounts, some form of network connectivity is required. This can be in the form of a transit gateway or VPC peering. In this demonstration, we will be using VPC peering. It is also necessary that the mount targets of the EFS and the Lambda function are in the same availability zones and have the same number of availability zones. In the sample infrastructure that we are going to build, we will place our subnets in us-east-1 AZ1 and AZ2. It is essential to note that in our infrastructure code, we will utilise the Availability Zone(AZ) ID instead of the AZ Name, as a name in one account may not correspond to the same AZ in another account.
Building the Infrastructure
The diagram below depicts the infrastructure that we will build, which will demonstrate EFS cross-account access.
Head to this Git repo to download the CloudFormation code that will build this infrastructure.
Pre-requisites
For the infrastructure code to work as is, the following are required:
- You have two (2) AWS accounts.
- You are working in the us-east-1 region.
- The execution roles that will create the resources should have an Administrative role or at least the necessary privileges to make the above resources, including the privilege to create VPC peering and describe EFS mounts.
If you don’t meet any of the above requirements, you need to make changes in the code.
Running the Code
There are two (2) sets of infrastructure code, one for each account. One set, which will create the EFS, is written in plain CloudFormation. The other set, which will create the Lambda function, is written using SAM (Serverless Application Model).
To build the infrastructure requires three (3) steps:
- Create the EFS and its corresponding resources.
We need to create the EFS and its corresponding resources first because they will be referenced later when we create the Lambda function in another account. To create the resources, create a stack using the template vpc-peering/cloudformation/efs-account/template.yaml
, e.g.:
aws cloudformation create-stack --stack-name EfsXAcctPeering --template-body file://template.yaml --capabilities "CAPABILITY_IAM" "CAPABILITY_NAMED_IAM" "CAPABILITY_AUTO_EXPAND" \
--parameters \
ParameterKey=PeerAccountId,ParameterValue=123456789012 \
ParameterKey=PeerRequesterRole,ParameterValue=/aws-reserved/sso.amazonaws.com/AWSReservedSSO_AdministratorAccess_123456 \
--profile my-efs-profile
The stack will expect the following parameters:
- PeerAccountId – the Lambda account ID.
- PeerRequesterRole – a role in the Lambda account that will create a peering request, so this should be the execution role in the Lambda account that you will use to create the Lambda resources. This role will be a Trusted Principal in a cross-account role created in this CloudFormation stack. The execution role will assume this role when the Lambda resources are created, allowing the EFS account to accept the peering request from the Lambda account.
The stack will create the following resources:
- A VPC, two (2) subnets, a security group and a route table.
- An EFS with two (2) mount targets.
- An EFS access point with root set to
/lambda
, and a POSIX and Root UID/GUID set to 1001. - A peering role that allows the action ‘ec2:AcceptVpcPeeringConnection‘
- Create the Lambda function and its corresponding resources.
. To create the resources, run the command sam deploy under the folder vpc-peering/cloudformation/lambda-account, e.g.:
sam deploy --parameter-overrides \
ParameterKey=PeerAccountId,ParameterValue=123456789013 \
ParameterKey=PeerVPCId,ParameterValue=vpc-12345678e48a1e4df \
ParameterKey=PeerRoleArn,ParameterValue=arn:aws:iam::123456789013:role/EfsXAcctPeering-PeeringRole-abc1234567 \
ParameterKey=EFSFileSystem,ParameterValue=fs-1234567890ec9ff1e \
ParameterKey=EFSAccessPoint,ParameterValue=fsap-1234567890626199a \
--profile my-lambda-profile
The stack will expect the following parameters:
- PeerAccountId – the EFS account ID.
- PeerVPCId – the EFS VPC ID.
- PeerRoleArn – the ARN of the role created in the EFS account. See PeerRequesterRole above.
- EFSFileSystem – the EFS file system ID created in Step 1.
- EFSAccessPoint – the EFS access point created in Step 1.
The stack will create the following resources:
- A VPC, two(2) subnets, a security group and a route table.
- A VPC peering.
- A route table entry that points to the VPC peering connection if the destination CIDR block is the EFS VPC CIDR.
- A Lambda function with a File System Config with a local mount path set to /mnt/lambda.
- Update the Route Table of the EFS VPC.
We need to add an entry in the EFS VPC route so traffic for the Lambda VPC will flow through the VPC peering connection.
You can use the same CloudFormation template, but this time you need to pass the peering connection ID, like so:
aws cloudformation update-stack --stack-name EfsXAcctPeering --template-body file://template.yaml --capabilities "CAPABILITY_IAM" "CAPABILITY_NAMED_IAM" "CAPABILITY_AUTO_EXPAND" \
--parameters \
ParameterKey=PeerAccountId,UsePreviousValue=true \
ParameterKey=PeerRequesterRole,UsePreviousValue=true \
ParameterKey=PeerConnectionId,ParameterValue=pcx-123456789096a3462 \
--profile my-efs-profile
Testing
The Lambda function will perform a simple task. If the file named file.txt doesn’t exist in the file system, it will create it containing the text ‘Hello, EFS!\n‘ and print out the content. If it exists, it will simply read the file and print out its contents.
You can test the function by invoking it directly from the console using the Test feature of the Lambda dashboard.
If the file does not exist, it should output similar to the one below. Take note of the Response. The ‘created_file‘ attribute should be set to true.
If the file exists, it should output similar to the one below. The ‘created_file’ attribute should be set to false this time.