AWS Cross Account S3 Access through Lambda
There are 2 ways to access S3 bucket from another account, using S3 bucket policy or Assume Role
I assume that the account number 111111111111
is holding the Lambda function, and the account number 222222222222
is holding the S3 bucket
I ssume that the S3 bukcet is encrypted with a KMS key (this is optional if S3 bucket is not encrypted)
Bukcet Policy method
account 111111111111, set this policy for the Lambda execution role :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
},
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey*"
],
"Resource": [
"arn:aws:kms::222222222222:key/KEY_ID"
]
}
]
}
account 222222222222, And this for the Bucket policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/lambda-execution-role"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
}
]
}
account 222222222222, For KMS Key Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey*",
"kms:ReEncrypt*"
],
"Resource": "*",
"Principal": { "AWS": "arn:aws:iam::111111111111:role/lambda-execution-role" }
}
]
}
sample Lambda code
import { S3Client, GetObjectCommand, ListObjectsCommand } from "@aws-sdk/client-s3";
export const handler = async (event, context) => {
const s3BucketName = "BUCKET_NAME";
const s3FileName = "123.txt";
const s3Client = new S3Client({
region: "us-west-2"
});
// ############################
const listObjectsParams = {
Bucket:s3BucketName
};
const listObjectsCommand = new ListObjectsCommand(listObjectsParams);
console.log("calling ListObjectsCommand");
const listObjectsOutput = await s3Client.send(listObjectsCommand);
console.log(listObjectsOutput.Contents);
// ############################
const getObjectCommand = new GetObjectCommand({
Bucket: s3BucketName,
Key: s3FileName,
});
console.log("calling GetObjectCommand");
const response = await s3Client.send(getObjectCommand);
// Process the file contents
const fileContents = await response.Body.transformToString();
console.log(fileContents);
// ############################
};
Assume Role
here we create a new role in account 222222222222
and assign the required policies to it then our lambda function assume that role when trying to access the S3 bucket.
account 222222222222, set this policy for the newly created role :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
}
]
}
and also we need to add this to the role Trusted entities
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/lambda-execution-role"
},
"Action": "sts:AssumeRole",
"Condition": {}
}
]
}
account 111111111111, set this policy for the Lambda execution role :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": [
"arn:aws:iam::222222222222:role/S3ROLE"
]
}
]
}
sample Lambda code
import { S3Client, GetObjectCommand, ListObjectsCommand } from "@aws-sdk/client-s3";
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
export const handler = async (event, context) => {
const roleArn = "arn:aws:iam::222222222222:role/S3ROLE";
const roleSessionName = "SessionName";
const s3BucketName = "BUCKET_NAME";
const s3FileName = "123.txt";
// Assume the role in the different account
const stsClient = new STSClient({ region: "us-west-2" });
const assumeRoleCommand = new AssumeRoleCommand({
RoleArn: roleArn,
RoleSessionName: roleSessionName,
});
const { Credentials } = await stsClient.send(assumeRoleCommand);
console.log(Credentials);
const s3Client = new S3Client({
region: "us-west-2",
credentials: {
accessKeyId: Credentials.AccessKeyId,
secretAccessKey: Credentials.SecretAccessKey,
sessionToken: Credentials.SessionToken
}
});
// ############################
const listObjectsParams = {
Bucket:s3BucketName
};
const listObjectsCommand = new ListObjectsCommand(listObjectsParams);
const listObjectsOutput = await s3Client.send(listObjectsCommand);
console.log(listObjectsOutput.Contents);
// ############################
try {
const getObjectCommand = new GetObjectCommand({
Bucket: s3BucketName,
Key: s3FileName,
});
const response = await s3Client.send(getObjectCommand);
// Process the file contents
// The Body object also has 'transformToByteArray' and 'transformToWebStream' methods.
const fileContents = await response.Body.transformToString();
console.log(fileContents);
} catch (err) {
console.error(err);
}
// ############################
};
sources: