aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.71k stars 3.94k forks source link

Cyclic reference if CloudWatch Rule and Lambda are in different stacks #7744

Open dalefrancum opened 4 years ago

dalefrancum commented 4 years ago

When creating a CloudWatch Rule in CDK, if the rule is in a different stack from the Lambda, this results in an error. The error can be reproduced by typing cdk ls. Adding this dependency (${reason}) would create a cyclic reference.

This is/was a similar issue with API Gateway. The CloudWatch problem was mentioned in the bottom of that issue. https://github.com/aws/aws-cdk/issues/3000

Reproduction Steps

import { App, Aws, Stack } from '@aws-cdk/core';
import awsIam = require('@aws-cdk/aws-iam');
import awsLambda = require('@aws-cdk/aws-lambda');
import awsEvents = require('@aws-cdk/aws-events');
import awsEventsTargets = require('@aws-cdk/aws-events-targets');

class LambdaStack extends Stack {
  lambdaRole: awsIam.Role;
  lambdaFunction: awsLambda.Function;

  constructor(app: App) {
    super(app, 'LambdaStack', {});
    this.lambdaRole = this.createLambdaRole();
    this.lambdaFunction = this.createLambdaFunction();
  }

  private createLambdaRole(): awsIam.Role {
    const lambdaRole: awsIam.Role = new awsIam.Role(this, 'LambdaRole', {
      roleName: 'LambdaRole',
      assumedBy: new awsIam.ServicePrincipal('lambda.amazonaws.com'),
    })

    const policyName = 'LambdaPolicy';
    const lambdaPolicy = new awsIam.Policy(this, policyName, {
      policyName: policyName,
      statements: [
        new awsIam.PolicyStatement({
          actions: [
            'logs:CreateLogStream',
            'logs:CreateLogGroup',
            'logs:DeleteLogStream',
            'logs:DeleteLogGroup',
            'logs:PutLogEvents',
          ],
          effect: awsIam.Effect.ALLOW,
          resources: [
            `arn:aws:logs:${Aws.REGION}:${Aws.ACCOUNT_ID}:log-group:/aws/lambda/LambdaFunction`,
          ],
        }),
      ],
    });
    lambdaRole.attachInlinePolicy(lambdaPolicy);

    return lambdaRole;
  }

  private createLambdaFunction(): awsLambda.Function {
    const lambdaFunction: awsLambda.Function = new awsLambda.Function(this, 'LambdaFunction', {
      runtime: awsLambda.Runtime.PYTHON_3_7,
      handler: 'lambda.lambda_handler',
      code: awsLambda.AssetCode.fromAsset('lambda.zip'),
      functionName: 'LambdaFunction',
      role: this.lambdaRole,
    });
    return lambdaFunction;
  }
}

class CloudWatchStack extends Stack {

  constructor(app: App, lambdaFunction: awsLambda.Function) {
    super(app, 'CloudWatchStack', {});
    this.createRule(lambdaFunction);
  }

  private createRule(lambdaFunction: awsLambda.Function) {

    const name = 'CwRule';
    const cloudWatchRule: awsEvents.Rule = new awsEvents.Rule(this, name, {
      ruleName: name,
      schedule: awsEvents.Schedule.expression('cron(0/5 11-23 ? * mon-fri *)'),
      enabled: false,
    });
    /* The addTarget() call will fail because the Lambda and CloudWatch rule are
    in separate stacks. */
    cloudWatchRule.addTarget(
      new awsEventsTargets.LambdaFunction(lambdaFunction, {
        event: awsEvents.RuleTargetInput.fromObject({}),
      }),
    );
  }
}

const app = new App();
const lambdaStack = new LambdaStack(app);
new CloudWatchStack(app, lambdaStack.lambdaFunction);

Error Log

Error: 'LambdaStack' depends on 'CloudWatchStack' (LambdaStack -> CloudWatchStack/CwRule/Resource.Arn). Adding this dependency (CloudWatchStack -> LambdaStack/LambdaFunction/Resource.Arn) would create a cyclic reference.

Environment

Other

This is/was a similar issue with API Gateway. The CloudWatch problem was mentioned in the bottom of that issue. https://github.com/aws/aws-cdk/issues/3000


This is :bug: Bug Report

ZeldaZach commented 4 years ago

Hey CDK Team, PVIT SDE here. My team is running into this same issue for a new stack we're developing with the Events Rules in one stack and the lambda, queue, and other components in another one.

We put a temporary workaround by having the Event Rules in the same file as the closest reach point (in our case, the SQS Stack). Would love to have better isolation for our stacks in the near future.

github-actions[bot] commented 2 years ago

This issue has not received any attention in 1 year. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

ZeldaZach commented 2 years ago

This is still an active issue that should be addressed