aws-cdk: [lambda] Circular dependency when trying to add policy to invoke itself

Reproduction Steps

const myLambda = new lambda.Function(this,'myLambda, {
...
    });
const myLambdaInvokePolicyStatement = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      actions: [ 'lambda:InvokeFunction' ],
      resources: [ myLambda.functionArn ]
   });
myLambda.addToRolePolicy(myLambdaInvokePolicyStatement);

What did you expect to happen?

CDK to be able to add the policy so my lambda can invoke itself.

What actually happened?

CDK is throwing ValidationError.

Stack failed: Error [ValidationError]: Circular dependency between resources: [...]

Environment

  • CLI Version : 1.69.0
  • Framework Version: 1.69.0
  • Node.js Version: v12.17.0
  • OS : MacOS 10.15.7
  • Language (Version): TypeScript (3.7.2)

Other


This is 🐛 Bug Report

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 17 (5 by maintainers)

Commits related to this issue

Most upvoted comments

The problem seems to be that with this dependency CloudFormation needs to create the lambda before the ServiceRole and like always the ServiceRole before the lambda like someone stated before.

We worked around that by introducing an additional policy into this circle:

import * as iam from '@aws-cdk/aws-iam';

[..]

const statement = new iam.PolicyStatement({
    actions: ['lambda:InvokeFunction'],
    resources: [ myLambda.functionArn ]
});
const policy = new iam.Policy(this, 'myLambda_policy', {
    statements: [statement]
});
policy.attachToRole(<iam.IRole> myLambda.role);

This works for us. I would guess that CloudFormation can then create it like this: ServiceRole -> Lambda -> Policy -> (Attach Policy to Role)

This is because the lambda permission node adds a GetAtt on the lambda function that is yet to be created. So the “Permission” cannot be created until the “Function” is created and the permission cannot be created without the “Function ARN” being available.

You can use the following workaround -

fn.role!.addToPrincipalPolicy(new PolicyStatement({
  actions: [ 'lambda:Invoke' ],
  resources: [ this.formatArn({
    service: 'lambda',
    resource: 'function',
    resourceName: 'myLambda',
  }) ],
}));

The problem seems to be that with this dependency CloudFormation needs to create the lambda before the ServiceRole and like always the ServiceRole before the lambda like someone stated before.

We worked around that by introducing an additional policy into this circle:

import * as iam from '@aws-cdk/aws-iam';

[..]

const statement = new iam.PolicyStatement({
    actions: ['lambda:InvokeFunction'],
    resources: [ myLambda.functionArn ]
});
const policy = new iam.Policy(this, 'myLambda_policy', {
    statements: [statement]
}
policy.attachToRole(<iam.IRole> myLambda.role);

This works for us. I would guess that CloudFormation can then create it like this: ServiceRole -> Lambda -> Policy -> (Attach Policy to Role)

I think there’s a missing bracket at the end of the initialisation of the const variable policy if you want to make an update there as well. 👍

This is because the lambda permission node adds a GetAtt on the lambda function that is yet to be created. So the “Permission” cannot be created until the “Function” is created and the permission cannot be created without the “Function ARN” being available.

You can use the following workaround -

fn.role!.addToPrincipalPolicy(new PolicyStatement({
  actions: [ 'lambda:Invoke' ],
  resources: [ this.formatArn({
    service: 'lambda',
    resource: 'function',
    resourceName: 'myLambda',
  }) ],
}));

Thanks that helped!