I'm trying to parametise the delimiter used by an Fn::Join, e.g. at first I had:
"Name" : { "Fn::Join" : [ ".", [
{ "Ref":"serviceName"}, { "Ref": "environment" } ] ] },
Which works well, but then I changed it to:
"Name" : { "Fn::Join" : [ {"Ref":"HostNameSeparator"}, [
{ "Ref":"serviceName"}, { "Ref": "environment" } ] ] },
I get the following error from the validation phase:
A client error (ValidationError) occurred when calling the
ValidateTemplate operation: Template error: every Fn::Join object
requires two parameters, (1) a string delimiter and (2) a list of
strings to be joined or a function that returns a list of strings
(such as Fn::GetAZs) to be joined.
Is it possible to do what I want, i.e. pass the Join delimiter as a template parameter?
(I've shortened the examples above for clarity sake, please ignore typos)
You can achieve this by using a CustomResource.
This CloudFormation Template is ready to run and illustrates how it could work. Delimiter is passed as a parameter and you'll find the joined string in the Outputs of the stack.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Example Join",
"Parameters": {
"Delimiter": {
"Type": "String"
}
},
"Resources": {
"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:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"cloudformation:DescribeStacks"
],
"Resource": "*"
}
]
}
}
]
}
},
"LambdaJoin": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"ZipFile": { "Fn::Join": ["\n", [
"var response = require('cfn-response');\n",
"exports.handler = function (event, context) {\n",
"if (event.RequestType === 'Delete') {\n",
"return response.send(event, context, response.SUCCESS, {}, event.PhysicalResourceId);\n",
"}\n",
"var delimiter = event.ResourceProperties.delimiter || '';\n",
"var strings = event.ResourceProperties.strings || [];\n",
"return response.send(event, context, response.SUCCESS, { string: strings.join(delimiter) }, event.PhysicalResourceId);\n",
"};\n"
]]}
},
"Handler": "index.handler",
"Runtime": "nodejs",
"Timeout": "10",
"Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] }
}
},
"CustomJoin": {
"Type": "Custom::Join",
"Version": "1.0",
"Properties": {
"ServiceToken": { "Fn::GetAtt": ["LambdaJoin", "Arn"] },
"delimiter": { "Ref": "Delimiter" },
"strings": ["first", "second", "third"]
},
"DependsOn": ["LambdaJoin"]
}
},
"Outputs": {
"JoinedString": {
"Value": { "Fn::GetAtt": ["CustomJoin", "string"] }
}
}
}
Related
I've been slamming my head against the wall for the past 2 days trying to get dynamic partitioning to work in the delivery stream I'm creating using a serverless application model template. I've tried multiple configurations, but it seems as soon as I fill in a prefix, enable dynamic partitioning and add a metadata extraction processor to get myself a field I can use as the prefix my stack stops working, I can generate alerts in aws and see the logs from the lambda processor I also added, but no files reach the bucket, I also can't seem to configure cloudwatch logs for the stream as well, since I've tried creating them through the script and manually and neither at least have logs pertaining to any error....
I'll leave my script here, hopefully I'm doing something wrong:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "An AWS Serverless Application.",
"Parameters": {
"BucketArn": {
"Type": "String"
}
},
"Resources": {
"DeliveryPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "K-POLICY",
"Roles": [
{
"Ref": "DeliveryRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
{
"Ref": "BucketArn"
}
]
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction",
"lambda:GetFunctionConfiguration"
],
"Resource": [
{
"Fn::GetAtt": [
"Processor",
"Arn"
]
}
]
}
]
}
}
},
"DeliveryRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"firehose.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
}
},
"DeliveryStream": {
"Type": "AWS::KinesisFirehose::DeliveryStream",
"Properties": {
"DeliveryStreamName": "K-STREAM",
"DeliveryStreamType": "DirectPut",
"ExtendedS3DestinationConfiguration": {
"BucketARN": {
"Ref": "BucketArn"
},
"Prefix": "!{partitionKeyFromQuery:symb}",
"ErrorOutputPrefix": "errors/!{firehose:random-string}/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd}",
"RoleARN": {
"Fn::GetAtt": [
"DeliveryRole",
"Arn"
]
},
"DynamicPartitioningConfiguration": {
"Enabled": true
},
"ProcessingConfiguration": {
"Enabled": true,
"Processors": [
{
"Type": "MetadataExtraction",
"Parameters": [
{
"ParameterName": "MetadataExtractionQuery",
"ParameterValue": "{symb: .TICKER_SYMBOL}"
},
{
"ParameterName": "JsonParsingEngine",
"ParameterValue": "JQ-1.6"
}
]
},
{
"Type": "Lambda",
"Parameters": [
{
"ParameterName": "LambdaArn",
"ParameterValue": {
"Fn::GetAtt": [
"Processor",
"Arn"
]
}
}
]
}
]
}
}
}
},
"Processor": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "K-PROCESSOR::KRATOS_PROCESSOR.Functions::FunctionHandler",
"FunctionName": "K-PROCESSOR",
"Runtime": "dotnetcore3.1",
"CodeUri": "./staging/app/3cs-int-k",
"MemorySize": 256,
"Timeout": 60,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole"
]
}
}
}
}
I am new to AWS CloudFormation, and I am trying to capture events from SQS Queue and place them in S3 bucket via AWS lambda. Flow of events is
SNS --> SQS <-- Lambda ---> S3 bucket.
I am trying to achieve above flow using cloudFormation template.I am getting below error message after deploying my CloudFormation template. Any help you can provide would be greatly appreciated. Thank you
11:51:56 2022-01-13 17:51:47,930 - INFO - ...
11:52:53 2022-01-13 17:52:48,511 - ERROR - Stack myDemoApp shows a rollback status ROLLBACK_IN_PROGRESS.
11:52:53 2022-01-13 17:52:48,674 - INFO - The following root cause failure event was found in the myDemoApp stack for resource EventStreamLambda:
11:52:53 2022-01-13 17:52:48,674 - INFO - Resource handler returned message: "Error occurred while GetObject. S3 Error Code: NoSuchKey.
S3 Error Message: The specified key does not exist. (Service: Lambda, Status Code: 400, Request
ID: 5f2f9882-a863-4a58-90bd-7e0d0dfdf4d5, Extended Request ID: null)" (RequestToken: 0a95acb4-a677-0a2d-d0bc-8b7487a858ad, HandlerErrorCode: InvalidRequest)
11:52:53 2022-01-13 17:52:48,674 - INFO - ..
MY lambda function is :
import json
import logging
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logging.basicConfig(level=logging.INFO,
format='%(asctime)s: %(levelname)s: %(message)s')
def lambda_handler(event, context):
logger.info(f"lambda_handler -- event: {json.dumps(event)}")
s3_bucket = boto3.resource("3")
event_message = json.loads(event["Records"][0]["body"])
s3_bucket.put_object(Bucket="S3DeployBucket", key="data.json", Body=json.dumps(event_message))
My complete CloudFormation template is :
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "myDemoApp Resource Stack",
"Mappings": {
},
"Parameters": {
"S3DeployBucket": {
"Default": "myDemoApp-deploy-bucket",
"Description": "Bucket for deployment configs and artifacts for myDemoApp",
"Type": "String"
},
"EnvName": {
"Description": "Platform environment name for myDemoApp",
"Type": "String"
},
"AuditRecordKeyArn": {
"Description": "ARN for audit record key encryption for myDemoApp",
"Type": "String"
},
"ParentVPCStack": {
"Description": "The name of the stack containing the parent VPC for myDemoApp",
"Type": "String"
},
"StackVersion": {
"Description": "The version of this stack of myDemoApp",
"Type": "String"
},
"EventLogFolderName": {
"Type": "String",
"Description": "folder name for the logs for the event stream of myDemoApp",
"Default": "event_log_stream"
},
"EventLogPartitionKeys": {
"Type": "String",
"Description": "The partition keys that audit logs will write to S3. Use Hive-style naming conventions for automatic Athena/Glue comprehension.",
"Default": "year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}"
},
"AppEventSNSTopicArn": {
"Description": "Events SNS Topic of myDemoApp",
"Type": "String"
},
"ReportingEventsRetentionDays": {
"Default": "2192",
"Description": "The number of days to retain a record used for reporting.",
"Type": "String"
}
},
"Resources": {
"AppEventSQSQueue": {
"Type": "AWS::SQS::Queue"
},
"AppEventSnsSubscription": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"TopicArn": {
"Ref": "AppEventSNSTopicArn"
},
"Endpoint": {
"Fn::GetAtt": [
"AppEventSQSQueue",
"Arn"
]
},
"Protocol": "sqs"
}
},
"S3DeployBucket": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain",
"UpdateReplacePolicy": "Retain",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"KMSMasterKeyID": {
"Ref": "AuditRecordKeyArn"
},
"SSEAlgorithm": "aws:kms"
}
}
]
},
"VersioningConfiguration": {
"Status": "Enabled"
},
"LifecycleConfiguration": {
"Rules": [
{
"ExpirationInDays": {
"Ref": "ReportingEventsRetentionDays"
},
"Status": "Enabled"
}
]
}
}
},
"EventStreamLogGroup": {
"Type": "AWS::Logs::LogGroup"
},
"EventLogStream": {
"Type": "AWS::Logs::LogStream",
"Properties": {
"LogGroupName": {
"Ref": "EventStreamLogGroup"
}
}
},
"EventStreamSubscriptionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "SNSSQSAccessPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": {
"Action": [
"sqs:*"
],
"Effect": "Allow",
"Resource": "*"
}
}
}
]
}
},
"EventDeliveryRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "sqs.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": {
"Ref": "AWS::AccountId"
}
}
}
}
]
}
}
},
"EventSqsQueuePolicy": {
"Type": "AWS::SQS::QueuePolicy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Id": "SqsQueuePolicy",
"Statement": [
{
"Sid": "Allow-SNS-SendMessage",
"Effect": "Allow",
"Principal": "*",
"Action": [
"sqs:SendMessage",
"sqs:ReceiveMessage"
],
"Resource": {
"Fn::GetAtt": [
"EventStreamLambda",
"Arn"
]
},
"Condition": {
"ArnEquals": {
"aws:SourceArn": {
"Ref": "EventSNSTopicArn"
}
}
}
}
]
},
"Queues": [
{
"Ref": "EventSNSTopicArn"
}
]
}
},
"EventDeliveryPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "sqs_delivery_policy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
{
"Fn::GetAtt": [
"S3DeployBucket",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"S3DeployBucket",
"Arn"
]
},
"/*"
]
]
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": {
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${EventStreamLogGroup}:log-stream:${EventLogStreamLogStream}"
}
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": [
{
"Ref": "AuditRecordKeyArn"
}
],
"Condition": {
"StringEquals": {
"kms:ViaService": {
"Fn::Join": [
"",
[
"s3.",
{
"Ref": "AWS::Region"
},
".amazonaws.com"
]
]
}
},
"StringLike": {
"kms:EncryptionContext:aws:s3:arn": {
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"S3DeployBucket",
"Arn"
]
},
"/*"
]
]
}
}
}
}
]
},
"Roles": [
{
"Ref": "EventDeliveryRole"
}
]
}
},
"EventStreamLambda": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "lambda_function.lambda_handler",
"MemorySize": 128,
"Runtime": "python3.8",
"Timeout": 30,
"FunctionName": "sqs_s3_pipeline_job",
"Role": {
"Fn::GetAtt": [
"SQSLambdaExecutionRole",
"Arn"
]
},
"Code": {
"S3Bucket": {
"Ref": "S3DeployBucket"
},
"S3Key": {
"Ref": "S3DeployBucket"
}
},
"TracingConfig": {
"Mode": "Active"
}
}
},
"SQSLambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "StreamLambdaLogs",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
},
{
"PolicyName": "SQSLambdaPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
"sqs:ChangeMessageVisibility"
],
"Resource":"*"
}
]
}
}
]
}
}
},
"Outputs": {
"VpcSubnet3ExportKey": {
"Value": {
"Fn::Sub": "${ParentVPCStack}-privateSubnet3"
}
}
}
}
SubscriptionRoleArn is only for kinesis:
This property applies only to Amazon Kinesis Data Firehose delivery stream subscriptions.
I am setting up an API HTTP Gateway (V2) with lambda integrations via Cloudformation, and everything has been working so far. I have 2 working integrations, but my third integration is not working: Everything looks fine from the API Gateway side (it lists the correct route with a link to the Lambda), but the API endpoint in the lambda is listed as "https://c59boisn2k.execute-api.eu-central-1.amazonaws.com/productionnull". When I try to call the route, it says "Not Found". The odd thing is that I am using the same template for all three integrations.
I was thinking it could be a "dependsOn" issue, but I think I have all the correct dependencies. I tried re-creating the stack from scratch and now two of the three functions say "null" in their URL while the API Gateway still states the correct routes. Can this be a 'dependsOn' problem?
Here's my template for a single integration:
{
"Resources": {
"api": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"Name": { "Ref": "AWS::StackName" },
"ProtocolType": "HTTP",
"CorsConfiguration": {
"AllowMethods": ["*"],
"AllowOrigins": ["*"]
}
}
},
"stage": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
"Description": { "Ref": "AWS::StackName" },
"StageName": "production",
"AutoDeploy": true,
"ApiId": { "Ref": "api" },
"AccessLogSettings": {
"DestinationArn": {
"Fn::GetAtt": ["stageLogGroup", "Arn"]
}
}
}
},
"getSignedS3LambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": {
"Fn::Sub": "${AWS::StackName}-getSignedS3"
},
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": ["lambda.amazonaws.com"]
},
"Action": ["sts:AssumeRole"]
}
]
},
"Policies": [
{
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "arn:aws:logs:*:*:*",
"Action": "logs:*"
},
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::euromomo.eu/uploads/*"]
}
]
}
}
]
}
},
"getSignedS3Lambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": ["getSignedS3LambdaRole"],
"Properties": {
"FunctionName": {
"Fn::Sub": "${AWS::StackName}-getSignedS3"
},
"Code": {
"S3Bucket": { "Ref": "operationsS3Bucket" },
"S3Key": { "Ref": "getSignedS3S3Key" }
},
"Runtime": "nodejs10.x",
"Handler": "index.handler",
"Role": { "Fn::GetAtt": ["getSignedS3LambdaRole", "Arn"] }
}
},
"getSignedS3Permission": {
"Type": "AWS::Lambda::Permission",
"DependsOn": ["api", "getSignedS3Lambda"],
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": { "Ref": "getSignedS3Lambda" },
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${api}/*/*"
}
}
},
"getSignedS3Integration": {
"Type": "AWS::ApiGatewayV2::Integration",
"DependsOn": ["getSignedS3Permission"],
"Properties": {
"ApiId": { "Ref": "api" },
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${getSignedS3Lambda.Arn}/invocations"
},
"PayloadFormatVersion": "2.0"
}
},
"getSignedS3Route": {
"Type": "AWS::ApiGatewayV2::Route",
"DependsOn": ["getSignedS3Integration"],
"Properties": {
"ApiId": { "Ref": "api" },
"RouteKey": "POST /getSignedS3",
"AuthorizationType": "NONE",
"Target": { "Fn::Sub": "integrations/${getSignedS3Integration}" }
}
}
}
}
After spending hours debugging this, I found that the problem was in my Lambda permission. I need to use the correct path in the permission.
This does not work:
arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${api}/*/*
This does work:
arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${api}/*/*/getSignedS3
I believe I could scope it even more to this:
arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${api}/*/POST/getSignedS3
This fixed all my problems and shows the correct path in the lambda web console.
Not sure where I am doing wrong. Below is my lambda code and cloudformation template.
Below is my lambda function:
import os
import boto3
def quickstart_lambda_handler(event, context):
print("event={}".format(event)) # nothing shows as k1:v1
print(os.environ) # nothing show in output as var1
print("environment variable var1: " + os.environ['var1'])
json_str = "{\"a1\":\"b1\"}"
bucket_name = "my-s3-bucket"
file_name = "qs.json"
s3_path = "cf-quickstart/" + file_name
s3 = boto3.resource("s3")
s3.Bucket(bucket_name).put_object(Key=s3_path, Body=json_str)
print("object stored in s3")
return s3_path
My cloud formation template is as below for lambda function and associated rule.
"QSLERole": {
"Description" : "Quick Start Lambda Execution Role",
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName" : "QSLERole",
"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:PutObject",
"Resource": "*"
}
]
}
}]
}
},
"QSLambdaF": {
"Description" : "Quick Start Lambda Function",
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "my-s3-bucket",
"S3Key": "generate-quickstartjson.zip"
},
"Handler": "generate-quickstartjson.quickstart_lambda_handler",
"Runtime": "python2.7",
"Timeout": "30",
"Environment" : {
"Variables" : {
"var1" : "var1 value"
}
},
"Role": {
"Fn::GetAtt": [
"QSLERole",
"Arn"
]
}
}
},
"ScheduledRule": {
"Description" :"Cloud Watch Quick Start Event Rule",
"DependsOn" : [
"QSLambdaF","EC2Instance"],
"Type": "AWS::Events::Rule",
"Properties" : {
"Description" : "CloudWatch Event for generating output",
"ScheduleExpression" : "rate(1 minute)",
"State": "ENABLED",
"Targets" : [
{
"Arn": { "Fn::GetAtt": [
"QSLambdaF", "Arn"] },
"Detail" : {
"test" : "value"
},
"Input": "{\"k1\":\"v1\"}",
"Id" : "QSScheduledRuleV1"
}
]
}
},
"PEILambda": {
"Description" : "Permission For Events To Invoke Lambda Function",
"Type": "AWS::Lambda::Permission",
"Properties" : {
"FunctionName": { "Ref": "QSLambdaF"},
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": [
"ScheduledRule", "Arn"] }
}
},
--- ec2 instance creation below with security group
Niether environment variables are available nor Input fields are available in lambda lambda.
"Input": "{\"k1\":\"v1\"}",
Please let me know where I am wrong. Thanks for the help.
works well as is. I was using wrong template file :))
I'm trying to create an Application Load Balancer in CloudFormation, with a target group that forwards traffic to EC2 instances. Here is the relevant snippet, where ELBSubnets, ECSCluster, taskdefinition, and VpcId are passed in as parameters:
"EcsElasticLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties" : {
"Subnets" : { "Ref" : "ELBSubnets" },
"SecurityGroups": [
{ "Ref": "ELBAccessSecurityGroup" }
]
}
},
"LoadBalancerListener": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
"DefaultActions": [{
"Type": "forward",
"TargetGroupArn": { "Ref": "TargetGroup" }
}],
"LoadBalancerArn": { "Ref": "EcsElasticLoadBalancer" },
"Port": 80,
"Protocol": "HTTP"
}
},
"TargetGroup": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {
"Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "TargetGroup" ] ] },
"Port": 80,
"Protocol": "HTTP",
"VpcId": { "Ref": "VpcId" }
},
"DependsOn": [ "EcsElasticLoadBalancer" ]
},
"service": {
"Type": "AWS::ECS::Service",
"Properties" : {
"Cluster": { "Ref": "ECSCluster" },
"DesiredCount": "1",
"LoadBalancers": [
{
"ContainerName": "main-app",
"ContainerPort": 3000,
"TargetGroupArn": { "Ref": "TargetGroup" }
}
],
"Role" : {"Ref":"ECSServiceRole"},
"TaskDefinition" : {"Ref":"taskdefinition"}
}
},
"ECSServiceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ecs.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "ecs-service",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:Describe*",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"ec2:Describe*",
"ec2:AuthorizeSecurityGroupIngress"
],
"Resource": "*"
}
]
}
}
]
}
}
I get the following error when creating the service:
The target group with targetGroupArn arn:aws:elasticloadbalancing:us-east-1:xxxxxxxx:targetgroup/AlbServiceStack-TargetGroup/6ba9c037c26cdb36 does not have an associated load balancer.
What am I missing? In the documentation there doesn't seem to be a way to specify a load balancer for the target group.
Got it working - the problem was twofold:
The following lines were missing from the Role PolicyDocument:
"elasticloadbalancing:DeregisterTargets"
"elasticloadbalancing:RegisterTargets"
The service needed "DependsOn": [ "LoadBalancerListener" ] as an additional attribute.
Updated template looks like this:
"EcsElasticLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties" : {
"Subnets" : { "Ref" : "ELBSubnets" },
"SecurityGroups": [
{ "Ref": "ELBAccessSecurityGroup" }
]
}
},
"LoadBalancerListener": {
"Type": "AWS::ElasticLoadBalancingV2::Listener",
"Properties": {
"DefaultActions": [{
"Type": "forward",
"TargetGroupArn": { "Ref": "TargetGroup" }
}],
"LoadBalancerArn": { "Ref": "EcsElasticLoadBalancer" },
"Port": 80,
"Protocol": "HTTP"
}
},
"TargetGroup": {
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
"Properties": {
"Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "TargetGroup" ] ] },
"Port": 80,
"Protocol": "HTTP",
"VpcId": { "Ref": "VpcId" }
},
"DependsOn": [ "EcsElasticLoadBalancer" ]
},
"service": {
"Type": "AWS::ECS::Service",
"DependsOn": [ "LoadBalancerListener" ],
"Properties" : {
"Cluster": { "Ref": "ECSCluster" },
"DesiredCount": "1",
"LoadBalancers": [
{
"ContainerName": "main-app",
"ContainerPort": 3000,
"TargetGroupArn": { "Ref": "TargetGroup" }
}
],
"Role" : {"Ref":"ECSServiceRole"},
"TaskDefinition" : {"Ref":"taskdefinition"}
}
},
"ECSServiceRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ecs.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "ecs-service",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:Describe*",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"ec2:Describe*",
"ec2:AuthorizeSecurityGroupIngress",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:RegisterTargets"
],
"Resource": "*"
}
]
}
}
]
}
}