Skip to content

(lambda): EFS and Lambda create cyclic reference when deployed in separate stacks #18759

@israelg99

Description

@israelg99

What is the problem?

EFS (particularly AccessPoint) and Lambda create cyclic reference when deployed in separate stacks.

Very related: #5760, #10942, #11245.
All of which are P1. Check the reproducible code in those issues as well.

Reproduction Steps

Here is a working example:

export class TheEfsLambdaStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'Vpc', {
      maxAzs: 2, // Default is all AZs in the region
    });

    // Create a file system in EFS to store information
    const fs = new efs.FileSystem(this, 'FileSystem', {
      vpc,
      removalPolicy: cdk.RemovalPolicy.DESTROY
    });

    const accessPoint = fs.addAccessPoint('AccessPoint',{
      createAcl: {
        ownerGid: '1001',
        ownerUid: '1001',
        permissions: '750'
      },
      path:'/export/lambda',
      posixUser: {
        gid: '1001',
        uid: '1001'
      }
    });

    // This lambda function is given access to our EFS File System
    const efsLambda = new lambda.Function(this, 'efsLambdaFunction', {
      runtime: lambda.Runtime.PYTHON_3_8,
      code: lambda.Code.fromAsset('lambda-fns'), 
      handler: 'message_wall.lambda_handler',
      vpc: vpc,
      filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg')
    });

    // defines an API Gateway Http API resource backed by our "efsLambda" function.
    let api = new apigw.HttpApi(this, 'Endpoint', {
      defaultIntegration: new integrations.LambdaProxyIntegration({
        handler: efsLambda
      })
    });

   new cdk.CfnOutput(this, 'HTTP API Url', {
     value: api.url ?? 'Something went wrong with the deploy'
   });
  }
}

The code breaks if Function is deployed in separate stack and the AccessPoint is passed in props, with the following error:

/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/stack.ts:395
      throw new Error(`'${target.node.path}' depends on '${this.node.path}' (${cycle.join(', ')}). Adding this dependency (${reason}) would create a cyclic reference.`);
            ^
Error: 'Stack2' depends on 'Stack' ("Stack2/lambda/lambda/ServiceRole/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget1", "Stack2/lambda/lambda/ServiceRole/DefaultPolicy/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget1", "Stack2/lambda/lambda/SecurityGroup/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget1", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget1", "Stack2/lambda/lambda/ServiceRole/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget2", "Stack2/lambda/lambda/ServiceRole/DefaultPolicy/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget2", "Stack2/lambda/lambda/SecurityGroup/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget2", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget2", "Stack2/lambda/lambda/ServiceRole/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget3", "Stack2/lambda/lambda/ServiceRole/DefaultPolicy/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget3", "Stack2/lambda/lambda/SecurityGroup/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget3", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsMountTarget3", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsSecurityGroup/from StackefsFileSystemEfsSecurityGroup2F9415D6:ALL PORTS", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsSecurityGroup/from StackfunctionSecurityGroupD8C55079:2049", "Stack2/lambda/lambda/Resource" depends on "Stack/efs/FileSystem/EfsSecurityGroup/from Stack2clipmodeldownloadSecurityGroupB2C2BEB1:2049"). Adding this dependency (Stack -> Stack2/lambda/lambda/SecurityGroup/Resource.GroupId) would create a cyclic reference.
    at Stack._addAssemblyDependency (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/stack.ts:395:13)
    at Object.addDependency (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/deps.ts:52:20)
    at Stack.addDependency (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/stack.ts:266:5)
    at resolveValue (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/private/refs.ts:100:12)
    at Object.resolveReferences (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/private/refs.ts:30:24)
    at Object.prepareApp (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/private/prepare-app.ts:30:3)
    at Object.synthesize (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/private/synthesis.ts:32:3)
    at App.synth (/Users/julian/work/project/node_modules/aws-cdk-lib/core/lib/stage.ts:90:23)
    at Object.<anonymous> (/Users/julian/work/project/bin/infra.ts:134:5)
    at Module._compile (internal/modules/cjs/loader.js:1072:14)

For more code insight check similar issues: #5760, #10942, #11245.

I can add simple self-contained reproducible code but it should be simple enough to infer.

What did you expect to happen?

Should work. We can pass Vpc in props across stacks, why EFS and others fail? The error is confusing as well.

What actually happened?

Fails as described above.

CDK CLI Version

2.10

Framework Version

No response

Node.js Version

14 LTS

OS

Mac

Language

Typescript

Language Version

TS 4.x

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-lambdaRelated to AWS LambdabugThis issue is a bug.effort/smallSmall work item – less than a day of effortp1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions