-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Describe the bug
When adding an S3 bucket as an event source to a lambda the account for the bucket is sourced from the stack not from the bucket configuration.
Expected Behavior
We have a stack intending to deploy, amongst other things, a Lambda to account "A"
We reference a Bucket
(which lives in account "B") using fromBucketAttributes
in the same stack and specified account "B" as the account
attribute (see reproduction steps). When hooking it up to the Lambda using addEventSource
we expect that it will generate IAM configuration specifying account "B" as part of the conditional grant.
Expected output from CDK:
├───┼───────────────────────────────────┼────────┼───────────────────────┼───────────────────────────────────┼───────────────────────────────────┤
│ + │ ${my-stack │ Allow │ lambda:InvokeFunction │ Service:s3.amazonaws.com │ "ArnLike": { │
│ │ │ │ │ │ "AWS:SourceArn": "arn:aws-cn:s3 │
│ │ │ │ │ │ :::the-bucket" │
│ │ │ │ │ │ }, │
│ │ │ │ │ │ "StringEquals": { │
│ │ │ │ │ │ "AWS:SourceAccount": "<ACCT B>" │
│ │ │ │ │ │ │
│ │ │ │ │ │ } │
└───┴───────────────────────────────────┴────────┴───────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
Current Behavior
Account "A" is defined as the "source account" which is incorrect. The Bucket lives in account "B" and was only referenced in the stack whose resources get deployed to "A".
Actual output from CDK:
├───┼───────────────────────────────────┼────────┼───────────────────────┼───────────────────────────────────┼───────────────────────────────────┤
│ + │ ${my-stack │ Allow │ lambda:InvokeFunction │ Service:s3.amazonaws.com │ "ArnLike": { │
│ │ │ │ │ │ "AWS:SourceArn": "arn:aws-cn:s3 │
│ │ │ │ │ │ :::the-bucket" │
│ │ │ │ │ │ }, │
│ │ │ │ │ │ "StringEquals": { │
│ │ │ │ │ │ "AWS:SourceAccount": "<ACCT A>" │
│ │ │ │ │ │ │
│ │ │ │ │ │ } │
└───┴───────────────────────────────────┴────────┴───────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
Reproduction Steps
import { DeploymentStack, DeploymentStackProps } from "@amzn/pipelines";
import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';
import { S3EventSource } from "aws-cdk-lib/aws-lambda-event-sources";
import { Bucket, EventType } from "aws-cdk-lib/aws-s3";
import { Construct } from "constructs";
export class BuggyStack extends DeploymentStack {
constructor(scope: Construct, id: string, props: DeploymentStackProps) {
super(scope, id, props);
let accountB = '123456789';
const foreignBucket =
Bucket.fromBucketAttributes(this, 'ImportedBucket', {
bucketArn: 'arn:aws:s3:::some-bucket-not-in-this-account',
// The account the bucket really lives in
account: accountB
});
let myLambda = new Function(this, 'my-function', {
runtime: Runtime.PYTHON_3_9,
code: Code.fromInline('print("hello world")'),
handler: 'whatever'
});
// This will generate the incorrect IAM bindings
myLambda.addEventSource(new S3EventSource(foreignBucket as Bucket,
{ events: [ EventType.OBJECT_CREATED ] }));
}
}
Possible Solution
To me, the most correct solution involves sourcing the bucket account from the bucket configuration itself. Since it's possible to specify the bucket account ID it's surprising when that isn't used when generating the IAM permissions.
I found a very bad workaround in the meantime in case anyone else is running into this:
type Writeable<T> = { -readonly [ P in keyof T ]: T[ P ] };
let stack: Writeable<Stack> = Stack.of(myBucket);
let accountA = stack.account;
stack.account = accountB;
myLambda.addEventSource(new S3EventSource(myBucket, { events: myEvents }));
stack.account = accountA;
Additional Information/Context
No response
CDK CLI Version
2.29.0 (build 47d7ec4)
Framework Version
No response
Node.js Version
v14.19.3
OS
Amazon Linux 2
Language
Typescript
Language Version
No response
Other information
No response