(events): EventBusPolicy fails with circular reference if PolicyStatement is not already resolved to JSON
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
When creating a `PolicyStatement` object that grants permissions to a principal, CDK throws an error with circular dependency on `grantPrincipal` method.
A PolicyStatement could be required when trying to give cross-account permissions in a `ResourcePolicy`.
### Expected Behavior
Policy is created successfully and principal is granted as specified.
### Current Behavior
Error throw when trying to synth any stack that contains the issue:
```shell
Error: Resolution error: Resolution error: Unable to resolve object tree with circular reference. Path: /Resources/${Token[EventbusPolicyTestStack.CFN.LogicalID.25]}/Properties/policyDocument/_principals/array/0/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal/grantPrincipal..
at resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1073)
at Object.resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:892)
at resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2755)
at Object.resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:892)
at resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2755)
at Object.resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:892)
at resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2755)
at Object.resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:892)
at resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2755)
at Object.resolve (/Users/neovasili/workspace/lab/eventbus-policy-test/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:892)
```
### Reproduction Steps
Here is a minimum Stack definition using `EventBusPolicy` that reproduces the issue when trying to do `cdk ls` or `cdk synth`:
```typescript
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as events from "aws-cdk-lib/aws-events";
export class EventbusPolicyTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const defaultEventBus = events.EventBus.fromEventBusName(this, "Default", "default");
new events.EventBusPolicy(this, "MyPolicy", {
eventBus: defaultEventBus,
statementId: "MyStatement",
statement: new iam.PolicyStatement({
principals: [
new iam.AccountPrincipal("123456789010"),
],
actions: [
"events:PutEvents",
],
resources: [
defaultEventBus.eventBusArn,
],
}),
});
}
}
```
Another example:
```typescript
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as events from "aws-cdk-lib/aws-events";
export class EventbusPolicyTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const defaultEventBus = events.EventBus.fromEventBusName(this, "Default", "default");
const policyFromJson = iam.PolicyStatement.fromJson({
Principal: {
AWS: "123456789010",
},
Action: [
"events:PutEvents",
],
Resource: [
defaultEventBus.eventBusArn,
],
});
new events.EventBusPolicy(this, "MyPolicy", {
eventBus: defaultEventBus,
statementId: "MyStatement",
statement: policyFromJson,
});
}
}
```
Another one for `BucketPolicy`:
```typescript
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as iam from "aws-cdk-lib/aws-iam";
export class EventbusPolicyTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const policyFromJson = iam.PolicyStatement.fromJson({
Principal: {
AWS: "123456789010",
},
Action: [
"events:PutEvents",
],
Resource: [
defaultEventBus.eventBusArn,
],
});
const testBucket = new s3.Bucket(this, "MyTestBucket");
new s3.CfnBucketPolicy(this, "CFN", {
bucket: testBucket.bucketName,
policyDocument: policyFromJson,
});
}
}
```
### Possible Solution
_No response_
### Additional Information/Context
In the case of `EventBusPolicy` is possible to pass principal in a different way using L1 construct that does not cause the issue:
```typescript
new events.CfnEventBusPolicy(this, "MyPolicy", {
statementId: "MyStatement",
action: "events:PutEvents",
principal: "123456789010",
});
```
### CDK CLI Version
2.63.2 (build e08e34a)
### Framework Version
_No response_
### Node.js Version
v16.14.2
### OS
Mac Ventura/Monterrey
### Language
Typescript
### Language Version
any version >= 4.6.x
### Other information
_No response_
Contributor guide
Research direction
Start by running the TypeScript reproduction with cdk ls or cdk synth, focusing on EventBusPolicy, PolicyStatement principals, and the circular grantPrincipal path in the error. Compare the object-based statement with PolicyStatement.fromJson and the L1 CfnEventBusPolicy example. Done means synthesis succeeds for the object-based policy and preserves the specified principal.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100