0
votes

I want to use CloudFormation to trigger Lambda when my CloudWatch function is called. I have the below, but it does not work.

CloudWatch rule created fine

"CloudWatchNewEc2": {
  "Type": "AWS::Events::Rule",
  "DependsOn": ["LambdaNewEc2"],
  "Properties": {
    "Description": "Triggered on new EC2 instances",
    "EventPattern": {
      "source": [
        "aws.ec2"
      ],
      "detail-type": [
        "AWS API Call via CloudTrail"
      ],
      "detail": {
        "eventSource": [
          "ec2.amazonaws.com"
        ],
        "eventName": [
          "RunInstances"
        ]
      }
    },
    "Targets": [
      {
        "Arn": {
          "Fn::GetAtt": ["LambdaNewEc2", "Arn"]
        },
        "Id": "NewEc2AutoTag"
      }
    ]
  }
},

Lambda created but is not triggered

"LambdaNewEc2": {
  "Type": "AWS::Lambda::Function",
  "DependsOn": ["S3Lambda", "IAMRoleLambda"],
  "Properties": {
    "Code": {
      "S3Bucket": {"Ref": "LambdaBucketName"},
      "S3Key": "skynet-lambda.zip"
    },
    "Description": "When new EC2 instances are created, auto tag them",
    "FunctionName": "newEc2AutoTag",
    "Handler": "index.newEc2_autoTag",
    "Role": {"Fn::GetAtt": ["IAMRoleLambda", "Arn"]},
    "Runtime": "nodejs6.10",
    "Timeout": "30"
  }
}

},

It seems like CloudWatch Target is not sufficient?

UPDATE (Full CloudFormation template)

{
  "Parameters": {
    "Environment": {
      "Type": "String",
      "Default": "Staging",
      "AllowedValues": [
        "Testing",
        "Staging",
        "Production"
      ],
      "Description": "Environment name"
    },
    "BucketName": {
      "Type": "String",
      "Default": "skynet-staging",
      "Description": "Bucket Name"
    },
    "LambdaBucketName": {
      "Type": "String",
      "Default": "skynet-lambda",
      "Description": "Lambda Bucket Name"
    },
    "Owner": {
      "Type": "String",
      "Description": "Owner"
    }
  },
  "Resources": {
    "S3Web": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Ref": "BucketName"
        },
        "WebsiteConfiguration": {
          "IndexDocument": "index.html",
          "RoutingRules": [
            {
              "RedirectRule": {
                "ReplaceKeyPrefixWith": "#"
              },
              "RoutingRuleCondition": {
                "HttpErrorCodeReturnedEquals": "404"
              }
            }
          ]
        },
        "AccessControl": "PublicRead",
        "Tags": [
          {
            "Key": "Cost Center",
            "Value": "Skynet"
          },
          {
            "Key": "Environment",
            "Value": {
              "Ref": "Environment"
            }
          },
          {
            "Key": "Owner",
            "Value": {
              "Ref": "Owner"
            }
          }
        ]
      }
    },
    "S3Lambda": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Ref": "LambdaBucketName"
        },
        "VersioningConfiguration": {
          "Status": "Enabled"
        },
        "Tags": [
          {
            "Key": "Cost Center",
            "Value": "Skynet"
          },
          {
            "Key": "Owner",
            "Value": {
              "Ref": "Owner"
            }
          }
        ]
      }
    },
    "CloudWatchNewEc2": {
      "Type": "AWS::Events::Rule",
      "DependsOn": ["LambdaNewEc2"],
      "Properties": {
        "Description": "Triggered on new EC2 instances",
        "EventPattern": {
          "source": [
            "aws.ec2"
          ],
          "detail-type": [
            "AWS API Call via CloudTrail"
          ],
          "detail": {
            "eventSource": [
              "ec2.amazonaws.com"
            ],
            "eventName": [
              "RunInstances"
            ]
          }
        },
        "Targets": [
          {
            "Arn": {
              "Fn::GetAtt": ["LambdaNewEc2", "Arn"]
            },
            "Id": "NewEc2AutoTag"
          }
        ]
      }
    },
    "IAMRoleLambda": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": "skynet-lambda-role",
        "AssumeRolePolicyDocument": {
          "Version" : "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                  "Service": [ "lambda.amazonaws.com" ]
              },
              "Action": [ "sts:AssumeRole" ]
            }
          ]
        },
        "ManagedPolicyArns": [
          "arn:aws:iam::aws:policy/AmazonEC2FullAccess",
          "arn:aws:iam::aws:policy/AWSLambdaFullAccess",
          "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess",
          "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
        ]
      }
    },
    "LambdaNewEc2": {
      "Type": "AWS::Lambda::Function",
      "DependsOn": ["S3Lambda", "IAMRoleLambda"],
      "Properties": {
        "Code": {
          "S3Bucket": {"Ref": "LambdaBucketName"},
          "S3Key": "skynet-lambda.zip"
        },
        "Description": "When new EC2 instances are created, auto tag them",
        "FunctionName": "newEc2AutoTag",
        "Handler": "index.newEc2_autoTag",
        "Role": {"Fn::GetAtt": ["IAMRoleLambda", "Arn"]},
        "Runtime": "nodejs6.10",
        "Timeout": "30"
      }
    }
  },
  "Outputs": {
    "WebUrl": {
      "Value": {
        "Fn::GetAtt": [
          "S3Web",
          "WebsiteURL"
        ]
      },
      "Description": "S3 bucket for web files"
    }
  }
}
1
Are the resources all in the same CloudFormation template? It would be helpful to see the whole template to understand what is happening better. - BryceH
@BryceH updated post with full template source - Jiew Meng
Once the stack is created, did you look at the resources through the management console to debug the configuration? Firstly, was the CloudWatch Events rule correctly created? Does the Lambda function execute correctly within a Test? When you say "it does not work", what exactly is "it" and what have you investigated? - John Rotenstein
@JohnRotenstein the event is created correctly with target as specified in cloudformation. I thought that should trigger the lambda function but it does not. I think I need to create a trigger on the lambda function but cannot find a way to do it in CloudFormation. - Jiew Meng
I'm confused -- do you wish to trigger the Lambda function when an instance is run (started), or do you wish to trigger it when the CloudFormation stack is created? If the latter, then use a AWS Lambda-backed Custom Resource. - John Rotenstein

1 Answers

1
votes

I managed to deploy your template into a CloudFormation stack (by removing the LambdaBucket and pointing to my own zip file). It seems to create all resource correctly.

It took about 10 minutes for the RunInstances event to appear in CloudTrail. It then successfully triggered the Rule, but the CloudWatch metrics for my rule showed a failed invocation because I faked a Lambda function for your template.

Once I edited the rule to point to a better function and re-tested, it worked fine.

Bottom line: Seems to work!