0
votes

I'm creating an ASG group which has a lifecyclehook for termination:

  LifecycleHook:
    Type: AWS::AutoScaling::LifecycleHook
    Properties: 
      AutoScalingGroupName: !Ref NodeGroup
      DefaultResult: CONTINUE
      HeartbeatTimeout: 60
      LifecycleHookName: !Sub "${AWS::StackName}-lifecycle-hook"
      LifecycleTransition: autoscaling:EC2_INSTANCE_TERMINATING

Now I create a lambda function as well:

  LambdaCreation:
    Type: "AWS::Lambda::Function"
    Properties: 
      Handler: "lambda_function.lambda_handler"
      Environment:
        Variables:
          aws_region : !Ref AWSRegion
      Role: !GetAtt LambdaExecutionRole.Arn
      Code: 
        S3Bucket: !Ref LambdaCodeBucket
        S3Key: "lambda-functions/function.zip"
      Runtime: "python3.6"
      Timeout: 60

On cloudwatch events, i created a rule for said event:

  CloudwatchEvent:
    Type: AWS::Events::Rule
    Properties: 
      Description: ASG scale-in event to lambda
      EventPattern: {
        "source": [
          "aws.autoscaling"
        ],
        "detail-type": [
          "EC2 Instance-terminate Lifecycle Action"
        ],
        "detail": {
          "AutoScalingGroupName": 
          [
            {
              "Fn::ImportValue" : 
              {
                "Fn::Sub" : "${RootStackName}-nodes-asg-name" 
              } 
            }
          ]
        }
      }
      State: ENABLED
      Targets: 
        - 
          Arn: 
            !GetAtt LambdaCreation.Arn
          Id: 
            !Ref LambdaCreation

But the lambda is never triggered.

Now, on AWS console I don't see a trigger on the designer. But if i add manually a cloudwatch trigger for the created rule, it starts working...

Why is the trigger on the lambda side not created? What am I missing?

Thanks all!

2

2 Answers

1
votes

I faced the exact same frustration. Only difference is that I was using terraform but that's irrelavant.

You are missing this:

{
  "Type" : "AWS::Lambda::Permission",
  "Properties" : {
      "Action" : String,
      "EventSourceToken" : String,
      "FunctionName" : String,
      "Principal" : String,
      "SourceAccount" : String,
      "SourceArn" : String
    }
}

The reason the "manual way" works because it creates the trigger AND the permission. When you provision stuff using IaC tools like Cloudformation/terraform, you need to explicitly specify this Lambda permission object.

1
votes

The below code snippet creates a lambda function and creates a cloudwatch event to trigger the lambda function with necessary privileges.

LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
  AssumeRolePolicyDocument:
    Version: '2012-10-17'
    Statement:
      - Effect: Allow
        Principal:
          Service:
            - lambda.amazonaws.com
        Action:
          - sts:AssumeRole
  Path: "/"
  Policies:
    - PolicyName: root
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - logs:*
            Resource: arn:aws:logs:*:*:*
          - Effect: Allow
            Action:
              - s3:ListBucket
            Resource: !Join [ '', [ 'arn:aws:s3:::', !Ref LambdaS3Bucket ] ]
          - Effect: Allow
            Action:
              - s3:GetObject
            Resource: !Join [ '', [ 'arn:aws:s3:::', !Ref LambdaS3Bucket, '/*' ] ]
          - Effect: Allow
            Action:
              - sts:GetCallerIdentity
            Resource: '*'
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
  Description: "Lambda function"
  FunctionName: !Ref LambdaFunctionName
  Handler: !Ref LambdaHandler
  Runtime: !Ref LambdaRuntime
  Timeout: !Ref LambdaTimeout
  MemorySize: !Ref LambdaMemorysize
  Role: !GetAtt LambdaExecutionRole.Arn
  Code:
    S3Bucket: !Ref LambdaS3Bucket
    S3Key: !Ref LambdaS3BucketKey
  Environment:
    Variables:
      time_interval_in_hours: !Ref TimeIntervalInHours
DependsOn: LambdaExecutionRole

CleanupEventRule:
Type: AWS::Events::Rule
Properties:
  Description: "Cloudwatch Rule"
  ScheduleExpression: !Ref CloudwatchScheduleExpression
  State: !Ref CloudWatchEventState
  Targets:
    - Arn: !Sub ${LambdaFunction.Arn}
      Id: "CleanupEventRule"
DependsOn: LambdaFunction

LambdaSchedulePermission:
Type: AWS::Lambda::Permission
Properties:
  Action: 'lambda:InvokeFunction'
  FunctionName: !Sub ${LambdaFunction.Arn}
  Principal: 'events.amazonaws.com'
  SourceArn: !Sub ${CleanupEventRule.Arn}
DependsOn: LambdaFunction