Pass parameters to AWS Cloudwatch event target lambda function - amazon-web-services

I want to pass parameters to my lambda function invoked by AWS Cloudwatch events. The parameter name is alarmActions and my CFT template for the event rule is as follows:
"LambdaInvokeScheduler": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "Scheduled Rule for invoking lambda function",
"EventPattern": {
"source": [
"aws.ecs"
],
"detail-type": [
"ECS Container Instance State Change"
],
"detail": {
"clusterArn": [
{ "Fn::GetAtt": ["WindowsCluster", "Arn"] }
]
}
},
"State": "ENABLED",
"Targets": [{
"Arn": { "Fn::GetAtt": ["AlarmCreationLambdaFunction", "Arn"] },
"Id": "AlarmCreationLambdaFunction",
"Input": { "Fn::Join" : ["", [ "{ \"alarmActions\": \"", { "Fn::Join" : [":", [ "arn:aws:sns", { "Ref" : "AWS::Region" }, { "Ref" : "AWS::AccountId" }, "CloudWatch"]] }, "\" }"]] }
}]
}
}
I have used the Input parameter to pass a JSON text. There is not much documentation around it. I just wanted to find the right way to do it.

I found the solution. I was referring the parameter in lambda in a wrong way.
My lambda function was like this:
def func(event, context, alarmActions)
{
print(alarmActions)
}
It worked when i made the following update:
def func(event, context)
{
print(event['alarmActions'])
}

Related

Passing values from CloudFormation to Swagger file

I'm creating my infrastructure using CloudFormation.
I'm hoping to create the API using "API Gateway Extensions to OpenAPI"
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html
Relevant code segment looks like this.
"MorningApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Description": {
"Fn::Sub": "${Stage}-MorningApi"
},
"Name": {
"Fn::Sub": "${Stage}-MorningApi"
},
"EndpointConfiguration": {
"Types": [
"REGIONAL"
]
},
"BodyS3Location": {
"Bucket" : "cf-morning",
"Key" : "nested-templates-stack/MorningApiStatusStackSwagger.json"
}
}
},
"MorningApiloadBalancer": {
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties": {
"Name": {
"Fn::Sub": "${Stage}-MorningApiloadBalancer"
},
"Subnets": [
{
"Ref": "PrivateASubnet"
},
{
"Ref": "PrivateBSubnet"
},
{
"Ref": "PrivateCSubnet"
}
],
"Type": {
"Ref": "ELBType"
},
"Scheme": "internal",
"IpAddressType": {
"Ref": "ELBIpAddressType"
}
}
}
I need to pass "DNSName" of the "MorningApiloadBalancer" to the swagger file located in the S3 location. I cannot find a way to do this.
Any help is appreciated.
I haven't tried using swagger with API gateway before.
Using Fn::Transform
Using the Swagger file stored in S3 using the Fn::Tranform macro, it basically takes the content of the swagger file and tranforms into cloudformation.
{
"APIGateway": {
"Type": "AWS::ApiGateway::RestApi",
"Name": "myapi",
"Properties": {
"Body": {
"Fn::Transform": {
"Name": "AWS::Include",
"Parameters": {
"Location": {
"Fn::Sub": "s3://${AWS::AccountId}-swagger-files/${SwaggerFile}"
}
}
}
}
}
}
}
Reference:
https://medium.com/#nabtechblog/integrating-swagger-with-aws-lambda-and-api-gateway-using-cloud-formation-macro-functions-7432dec50dd
Inline swagger definition
I saw an example where the swagger definition is embedded into the cloudformation template. If you do so, you can use intrinsic functions inside the swagger definition. In your case, you can use Ref to get the dns name of the load balancer.
"GreetingApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "Greeting API",
"Description": "API used for Greeting requests",
"FailOnWarnings": true,
"Body": {
"swagger": "2.0",
"paths": {
"/greeting": {
"get": {
"x-amazon-apigateway-integration": {
"uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"GreetingLambda",
"Arn"
]
},
"/invocations"
]
]
}
}
}
}
}
}
}
}
Reference:
https://blog.jayway.com/2016/09/18/introduction-swagger-cloudformation-api-gateway/
Getting DNS Name
I think you know this already.
"Fn::GetAtt" : [ "MorningApiloadBalancer" , "DNSName" ]
Hope this helps.

CloudFormation Rules for StepFunctions

I have used CloudFormation to create CloudWatch event rules and associated permissions to run lambdas, but I can't find similar documentation for starting Step Function executions. For example, if the following is correct for lambdas, what is the analog for Step Functions?
"ExecuteLambda1" : {
"Type" : "AWS::Events::Rule",
"Properties" : {
"Name" : "rule-1",
"Description" : "Run Lambda1",
"ScheduleExpression": "rate(15 minutes)",
"State": "DISABLED",
"Targets": [{
"Arn": "arn:Lambda1Arn",
"Id": "Lambda1Arn1"
}]
}
},
"PermissionForExecuteLambda1": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": "arn:Lambda1Arn",
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["ExecuteLambda1", "Arn"] }
}
}
I assume you need to change "FunctionName" to point to the Step Function, and the "Action" to "StartExecution," but my attempts at guessing didn't work out. Any help would be appreciated. Thanks.

CloudFormation AWS::Events::Rule: Encountered unsupported property Id

Trying to create AWS Lambda Function using CloudFormation. When creating schedule rule as target for the AWS Lambda Function I'm getting the following error:
Lambda function needs targets
My template looks like:
"ScheduledRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "ScheduledRule",
"ScheduleExpression": "rate(10 minutes)",
"State": "ENABLED",
"Targets": [{
"Arn": {
"Fn::GetAtt": ["LambdaFunction", "Arn"]
}
}],
"Id": "id",
"RoleArn": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
}
}
},
The nesting of your event rule is wrong. In your template Id and RoleArn are children of Properties, whereas they should be children of your target. A correct template would be:
"ScheduledRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "ScheduledRule",
"ScheduleExpression": "rate(10 minutes)",
"State": "ENABLED",
"Targets": [{
"Arn": {
"Fn::GetAtt": ["LambdaFunction", "Arn"]
},
"Id": "id",
"RoleArn": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
}
}]
}
},

Unable to set environment variable for aws lambda function executed as AWS::CloudFormation::CustomResource

I am trying to set the environment variable which takes it's value at runtime through my CloudFormation template json for CustomResource. So that later it executes a python lambda and I can read the environment variable in the lambda and process some data.
I want my python lambda to be able to read this variable inside os.environ
Following is my Cloudformation for CustomResource
"TriggerRedshiftSetupLambda": {
"Type": "AWS::CloudFormation::CustomResource",
"Version": 1.0,
"Properties": {
"Environment": {
"Variables": {
"AHost": {
"Fn::GetAtt" : [ "A", "Endpoint.Address" ]
},
"APort": {
"Fn::GetAtt" : [ "A", "Endpoint.Port" ]
}
}
},
"ServiceToken": {
"Fn::GetAtt" : [ "ASetupLambda", "Arn" ]
}
}
}
Here is my lambda code using the variable
def lambda_handler(event, context):
print(os.environ)
print(os.environ['AHost'])
The 1st print statement prints the entire environment variables list but doesn't have any key / value pair for 'AHost'
Am I doing something wrong? How to initialize environment variables through customresource for lambda correctly?
Setting environment variables through the custom resource definition seems not to be supported. What you are setting is the properties section for the actual invocation (so event data).
So taking your template, your configuration should be accessible under the following path.
event['ResourceProperties']['Environment']['Variables']['AHost']
As stated by #jens above it's not possible to set environment variables under os.environ using the CustomResource CloudFormation.
Instead, the Lambda CloudFormation needs to define those values -
"RedshiftSetupLambda": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": { "Fn::Sub": "XYZ-${Branch}" },
"S3Key": { "Fn::Sub": "XYZ-${Commit}.zip" }
},
"Description": "Setup Lambda",
"FunctionName": { "Fn::Sub": "${BlockId}-setup-${Branch}" },
"Handler": "setup.lambda_handler",
"KmsKeyArn": {
"Fn::ImportValue": {
"Fn::Sub": "${BlockId}-Common-RolesKeys-${Branch}-KMSKeyArn"
}
},
"Role": {
"Fn::ImportValue": {
"Fn::Sub": "${BlockId}-Common-RolesKeys-${Branch}-LambdaIAMRoleArn"
}
},
"Runtime": "python2.7",
"Timeout": 30,
"VpcConfig": {
"SecurityGroupIds": [ {"Ref": "SecurityGroup"} ],
"SubnetIds": [
{ "Fn::ImportValue": "VPCCreate-PrivateSubnet1Id" },
{ "Fn::ImportValue": "VPCCreate-PrivateSubnet2Id" },
{ "Fn::ImportValue": "VPCCreate-PrivateSubnet3Id" }
]
},
"Environment": {
"Variables": {
"DB_USERNAME": {
"Ref": "MasterUsername"
},
"AHOST": {
"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Address"]
},
"APORT": {
"Fn::GetAtt": ["RedshiftCluster", "Endpoint.Port"]
},
"CLUSTER_IDENTIFIER": {
"Ref": "RedshiftCluster"
}
}
}
}
}
They can be accessed this way:
print(os.environ['AHOST'])

AWS Cloudformation: Cross Stack Reference

I have the following stack structure.
ParentStack:
ChildStack1
ChildStack2
What I need to do is to pass LambdaExecutionRole arn created in ChildStack2, having ChildStack2Name as a parameter, to ChildStack1.
Here are the snippets of my script
Parent Stack:
"Resources": {
"ChildStack1": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"ChildStack2Name": {"Fn::GetAtt": ["ChildStack2", "Outputs.StackName"]}
},
....
ChildStack1:
"Parameters" {
"ChildStack2Name": {
"Type": "String",
"Description": "Name of commons stack"
},
...
"Resources": {
"Role": {
HERE I NEED A LambdaExecutionRole ARN CREATED IN CHILDSTACK2
}
}
ChildStack2
"Resources": {
"LambdaExecutionRole": {
.....
}
}
"Outputs": {
"LambdaExecutionRole": {
"Value": {"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]},
"Export": {
"Name": {
"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]
}
}
},
"StackName": {
"Value": {"Ref": "AWS::StackName"},
"Export": {
"Name": {"Ref": "AWS::StackName"}
}
}
}
}
I'm new to cloudformation and probably these snippets could be refactored in some way.
Thank you.
First, you should output the LambdaExecutionRole's ARN from the template. Example below is assuming the resource LambdaExecutionRole in ChildStack2 is an AWS::IAM::Role type and the output name is LambdaExecutionRoleARN.
"Outputs": {
"LambdaExecutionRole": {
"Value": {"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]},
"Export": {
"Name": {
"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]
}
}
},
"LambdaExecutionRoleARN": {
"Value": {"Fn::GetAtt" : ["LambdaExecutionRole", "Arn"]}
},
Next, in ChildStack1, add another parameter to hold the ARN value that will be passed from Parent Stack. Then you can Ref the parameter into Role resource.
"Parameters" {
"ChildStack2Name": {
"Type": "String",
"Description": "Name of commons stack"
},
"LambdaExecutionRoleArn": {
"Type": "String",
"Description": "Lambda Execution Role's ARN"
},
...
"Resources": {
"Role": {
// Here use { "Ref": "LambdaExecutionRoleArn"} to get the value of the parameters
}
}
Then in your Parent Stack, you can pass the ARN from ChildStack2 to ChildStack 1 using Fn::GetAtt
"Resources": {
"ChildStack1": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"ChildStack2Name": {"Fn::GetAtt": ["ChildStack2", "Outputs.StackName"]}
"LambdaExecutionRoleArn": {"Fn::GetAtt": ["ChildStack2", "Outputs.LambdaExecutionRoleARN"]}
},
....