Access AWS S3 from Lambda within VPC
There is another solution related to VPC endpoints.
On AWS Console, choose VPC service and then Endpoints. Create a new endpoint, associate it to s3 service
and then select the VPC and Route Table.
Then select access level (full or custom) and it will work.
There's another issue having to do with subnets and routes that is not addressed in the other answers, so I am creating a separate answer with the proviso that all the above answers apply. You have to get them all right for the lambda function to access S3.
When you create a new AWS account which I did last fall, there is no route table automatically associated with your default VPC (see Route Tables -> Subnet Associations in the Console).
So if you follow the instructions to create an Endpoint and create a route for that Endpoint, no route gets added, because there's no subnet to put it on. And as usual with AWS you don't get an error message...
What you should do is create a subnet for your lambda function, associate that subnet with the route table and the lambda function, and then rerun the Endpoint instructions and you will, if successful, find a route table that has three entries like this:
Destination Target
10.0.0.0/16 Local
0.0.0.0/0 igw-1a2b3c4d
pl-1a2b3c4d vpce-11bb22cc
If you only have two entries (no 'pl-xxxxx' entry), then you have not yet succeeded.
In the end I guess it should be no surprise that a lambda function needs a subnet to live on, like any other entity in a network. And it's probably advisable that it not live on the same subnet as your EC2 instances because lambda might need different routes or security permissions. Note that the GUI in lambda really wants you to have two subnets in two different AZs which is also a good idea.
With boto3, the S3 urls are virtual by default, which then require internet access to be resolved to region specific urls. This causes the hanging of the Lambda function until timeout.
To resolve this requires use of a Config
object when creating the client, which tells boto3 to create path based S3 urls instead:
import boto3
import botocore
client = boto3.client('s3', 'ap-southeast-2', config=botocore.config.Config(s3={'addressing_style':'path'}))
Note that the region in the call must be the region to which you are deploying the lambda and VPC Endpoint.
Then you will be able to use the pl-xxxxxx
prefix list for the VPC Endpoint within the Lambda's security group, and still access S3.
Here is a working CloudFormation script that demonstrates this. It creates an S3 bucket, a lambda (that puts records into the bucket) associated to a VPC containing only private subnets and the VPC Endpoint, and necessary IAM roles.