I am pushing 5 messages to the SQS and expecting that my lambda should get those 5 messages and just log, when i trigger the function I see that the publisher lambda is pushing 5 messages to the sqs but the consumer lambda is not getting those 5 messages instead it is getting only one. Any idea why?
# publisher lambda configuration
fetchUserDetails:
handler: FetchUserDetails/index.fetchUserDetails
timeout: 900
package:
individually: true
artifact: "./dist/FetchUserDetails.zip"
reservedConcurrency: 175
environment:
SEND_EMAIL_SQS_URL: ${self:custom.EMAILING_SQS_URL}
# consumer lambda configuration
sendEmails:
handler: SendEmails/index.sendEmails
timeout: 30
package:
individually: true
artifact: "./dist/SendEmails.zip"
events:
- sqs:
arn:
Fn::GetAtt:
- SendEmailSQS
- Arn
batchSize: 1
# SQS configuration
SendEmailSQS:
Type: "AWS::SQS::Queue"
Properties:
QueueName: ${self:custom.EMAILING_SQS_NAME}
FifoQueue: true
VisibilityTimeout: 45
ContentBasedDeduplication: true
RedrivePolicy:
deadLetterTargetArn:
Fn::GetAtt:
- SendEmailDlq
- Arn
maxReceiveCount: 15
# publisher lambda code
const fetchUserDetails = async (event, context, callback) => {
console.log("Input to the function-", event);
/* TODO: 1. fetch data applying all the where clauses coming in the input
* 2. push each row to the SQS */
const dummyData = [
{
user_id: "1001",
name: "Jon Doe",
email_id: "test1#test.com",
booking_id: "1"
},
{
user_id: "1002",
name: "Jon Doe",
email_id: "test2#test.com",
booking_id: "2"
},
{
user_id: "1003",
name: "Jon Doe",
email_id: "test3#test.com",
booking_id: "3"
},
{
user_id: "1004",
name: "Jon Doe",
email_id: "test4#test.com",
booking_id: "4"
},
{
user_id: "1005",
name: "Jon Doe",
email_id: "test5#test.com",
booking_id: "5"
}
];
try {
for (const user of dummyData) {
const params = {
MessageGroupId: uuid.v4(),
MessageAttributes: {
data: {
DataType: "String",
StringValue: JSON.stringify(user)
}
},
MessageBody: "Publish messages to send mailer lambda",
QueueUrl:
"https://sqs.ap-southeast-1.amazonaws.com/344269040775/emailing-sqs-dev.fifo"
};
console.log("params-", params);
const response = await sqs.sendMessage(params).promise();
console.log("resp-", response);
}
return "Triggered the SQS queue to publish messages to send mailer lambda";
} catch (e) {
console.error("Error while pushing messages to the queue");
callback(e);
}
};
# consumer lambda code, just some logs
const sendEmails = async event => {
console.log("Input to the function-", event);
const allRecords = event.Records;
const userData = event.Records[0];
const userDataBody = JSON.parse(userData.messageAttributes.data.stringValue);
console.log("records-", allRecords);
console.log("userData-", userData);
console.log("userDataBody-", userDataBody);
console.log("stringified log-", JSON.stringify(event));
};
# permissions lambda has
- Effect: "Allow"
Action:
- "sqs:SendMessage"
- "sqs:GetQueueUrl"
Resource:
- !GetAtt SendEmailSQS.Arn
- !GetAtt SendEmailDlq.Arn
Your consumer is only looking at one record:
const userData = event.Records[0];
It should loop through all Records and process their messages, rather than only looking at Records[0].
Related
I want to make the WAF as IP whitelist with wafv2
Currently My code is here
import { aws_wafv2 as wafv2 } from 'aws-cdk-lib';
const wafacl = new wafv2.CfnWebACL(this, "MyCfnWebAll",{
name: `ee-${targetEnv}-waf`,
scope: "REGIONAL",
defaultAction: {
allow:{
customRequestHandling: {
insertHeaders: [{
name: 'my_allow_name',
value: 'my_allow_value',
}],
},
}
},
visibilityConfig:{
cloudWatchMetricsEnabled: false,
metricName: 'metricName',
sampledRequestsEnabled: false
}
});
new wafv2.CfnWebACLAssociation(this, 'WebACLAssociation', {
webAclArn: wafacl.attrArn,
resourceArn: lb.loadBalancerArn
})
const cfnIPSet = new wafv2.CfnIPSet(this, 'MyCfnIPSet', {
addresses: ['23.186.72.133/32','143.32.1.45/32'],
ipAddressVersion: 'IPV4',
scope: 'REGIONAL',
description: 'description',
name: `ss-${targetEnv}-ipset`,
});
It can make the WAF ,association with the LoadBalancer and ipSet
However it lacks rules
I am searching the samples to code rule , however I could't find the good clue.
What I want to do is equivalent to this,
Add my own rules and rule groups
choose doesn't match the statement(NOT)
set IPset
Any help is appreciated.
For now, my reference is these.
https://docs.aws.amazon.com/cdk/api/v1/docs/#aws-cdk_aws-wafv2.CfnWebACL.RuleProperty.html
https://docs.aws.amazon.com/cdk/api/v1/docs/#aws-cdk_aws-wafv2.CfnWebACL.html
I made the code like this below.
making the rule and try to set this in rules of wafv2.CfnWebACL
const ruleProperty: wafv2.CfnWebACL.RuleProperty = {
name: 'name',
priority: 123,
statement: {
ipSetReferenceStatement: {
arn: cfnIPSet.attrArn
}
},
visibilityConfig: {
cloudWatchMetricsEnabled: false,
metricName: 'metricName',
sampledRequestsEnabled: false,
}
}
const wafacl = new wafv2.CfnWebACL(this, "MyCfnWebAll",{
name: `ss-${targetEnv}-waf`,
scope: "REGIONAL",
rules:[ruleProperty], ## add here
There comes the error like this
Resource handler returned message: "Error reason: You have used none or multiple values for a field that requi
res exactly one value., field: RULE, parameter: Rule (Service: Wafv2, Status Code: 400, Request ID: ce79fc3b-c
b96-4856-9d9f-12ea39407091, Extended Request ID: null)" (RequestToken: f2ef3c98-382f-1b21-2351-e3861e418623, H
andlerErrorCode: InvalidRequest)
Please use this as a reference to write the rules.
https://github.com/aws/aws-cdk/issues/6056#issuecomment-581583976
IPSet is written like this.
{
name: "CustomAllowIpSetRule",
priority: 1,
statement: {
ipSetReferenceStatement: {
arn: "xxxxxxx"
},
},
action: { allow: {} },
visibilityConfig: {
sampledRequestsEnabled: true,
cloudWatchMetricsEnabled: true,
metricName: "CustomAllowIpSetRule",
},
},
I am trying to build a express rest-api with the serverless framework with the following code. I have a working POST request method to the path /fruits but the following GET request method throws a 502 Bad Gateway error.
const serverless = require('serverless-http');
const express = require('express');
const app = express();
const AWS = require('aws-sdk');
...
const dynamoDB = new AWS.DynamoDB.DocumentClient();
app.get('/fruits/:fruitName', (req, res) => {
const params = {
TableName: TABLE_NAME,
Key: {
fruitName: req.params.fruitName,
},
}
dynamoDB.get(params, (err, res) => {
if (err) {
console.log(error);
res.status(400).json({ error: 'Could not get fruit' });
}
if (res.Item) {
const { fruitName, biName} = res.Item;
res.json({ fruitName, biName});
} else {
res.status(404).json({ error: "Fruit not found" });
}
})
})
...
module.exports.handler = serverless(app);
I have set up a serverless.yml as follows
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: us-west-2
iamRoleStatements:
- Effect: 'Allow'
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["FruitsTable", "Arn" ] }
environment:
TABLE_NAME: 'fruits'
resources:
Resources:
FruitsTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: fruitName
AttributeType: S
KeySchema:
- AttributeName: fruitName
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: 'fruits'
functions:
app:
handler: index.handler
events:
- httpApi: 'GET /fruits/{fruitName}'
- httpApi: 'POST /fruits'
Any help is much appreciated.
The issue was identical variable naming causing a overwrite. And the following would fix that.
app.get('/fruits/:fruitName', (req, res) => {
const params = {
TableName: TABLE_NAME,
Key: {
fruitName: req.params.fruitName,
},
}
dynamoDB.get(params, (err, result) => {
if (err) {
console.log(error);
res.status(400).json({ error: 'Could not get fruit' });
}
if (result.Item) {
const { fruitName, biName} = result.Item;
res.json({ fruitName, biName});
} else {
res.status(404).json({ error: "Fruit not found" });
}
})
})
I am following the instructions here to setup an event bridge: https://eventbus-cdk.workshop.aws/en/04-api-gateway-service-integrations/01-rest-api/rest-apis.html
Based on the error message, the error is coming from this line of code: languageResource.addMethod("POST", new apigw.Integration({
I am not sure what is causing this issue because this is an example given by AWS and should work, but it does not.
I can build it but it fails with the following error on cdk deploy:
CREATE_FAILED | AWS::ApiGateway::Method | MyRestAPI/Default/{language}/POST (MyRestAPIlanguagePOSTB787D51A) Invalid Resource identifier specified (Service: AmazonApiGateway; Status Code: 404; Error Code: NotFoundException;
The code is below:
const myLambda = new lambda.Function(this, "MyEventProcessor", {
code: new lambda.InlineCode("def main(event, context):\n\tprint(event)\n\treturn {'statusCode': 200, 'body': 'Hello, World'}"),
handler: "index.main",
runtime: lambda.Runtime.PYTHON_3_7
})
const bus = new events.EventBus(this, `pwm-${this.stage}-MdpEventBus`)
new cdk.CfnOutput(this, "PwmMdpEventBus", {value: bus.eventBusName})
new events.Rule(this, `PwmMdpEventBusRule`, {
eventBus: bus,
eventPattern: {source: [`com.amazon.alexa.english`]},
targets: [new targets.LambdaFunction(myLambda)]
})
const apigwRole = new iam.Role(this, "MYAPIGWRole", {
assumedBy: new iam.ServicePrincipal("apigateway"),
inlinePolicies: {
"putEvents": new iam.PolicyDocument({
statements: [new iam.PolicyStatement({
actions: ["events:PutEvents"],
resources: [bus.eventBusArn]
})]
})
}
});
const options = {
credentialsRole: apigwRole,
requestParameters: {
"integration.request.header.X-Amz-Target": "'AWSEvents.PutEvents'",
"integration.request.header.Content-Type": "'application/x-amz-json-1.1'"
},
requestTemplates: {
"application/json": `#set($language=$input.params('language'))\n{"Entries": [{"Source": "com.amazon.alexa.$language", "Detail": "$util.escapeJavaScript($input.body)", "Resources": ["resource1", "resource2"], "DetailType": "myDetailType", "EventBusName": "${bus.eventBusName}"}]}`
},
integrationResponses: [{
statusCode: "200",
responseTemplates: {
"application/json": ""
}
}]
}
const myRestAPI = new apigw.RestApi(this, "MyRestAPI");
const languageResource = myRestAPI.root.addResource("{language}");
languageResource.addMethod("POST", new apigw.Integration({
type: apigw.IntegrationType.AWS,
uri: `arn:aws:apigateway:${cdk.Aws.REGION}:events:path//`,
integrationHttpMethod: "POST",
options: options,
}),
{
methodResponses: [{
statusCode: "200"
}],
requestModels: {"application/json": model.getModel(this, myRestAPI) },
requestValidator: new apigw.RequestValidator(this, "myValidator", {
restApi: myRestAPI,
validateRequestBody: true
})
})
In the AWS example, they are encapsulating your code inside
export class MyCdkAppStack extends cdk.Stack {
...
}
Are you missing that encapsulation? I noticed your sample code didn't include it. Because when you execute const myRestAPI = new apigw.RestApi(this, "MyRestAPI"); the this should refer to the MyCdkAppStack instance.
I Have a lambda function created using CFN which looks like this:
InitializeDynamoDBLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
const AWS = require("aws-sdk");
const response = require("cfn-response");
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = function(event, context) {
let DynamoTableName = event.ResourceProperties.DynamoTable;
let KeyJSON = JSON.parse(event.ResourceProperties.KeyJSON);
let ValueJSON = JSON.parse(event.ResourceProperties.ValueJSON);
for(key in KeyJSON){
var params = {
TableName: DynamoTableName,
Item: {
'Key': KeyJSON[key],
'Value': JSON.stringify(ValueJSON[key])
}
};
docClient.put(params, function(err, data) {
if (err) {
console.log(err);
response.send(event, context, response.FAILED, {});
}
else {
response.send(event, context, response.SUCCESS, {});
}
});
}
};
Handler: index.handler
Role: !GetAtt 'LambdaExecutionRole.Arn'
Runtime: nodejs12.x
Timeout: 60
This Lambda is initialized using a CUSTOM resource like this:
InitializeDB:
Type: Custom::InitializeDynamoDBLambda
Properties:
ServiceToken:
Fn::GetAtt: [ InitializeDynamoDBLambda , "Arn" ]
DynamoTable: !Ref TenantLevelDBname
KeyJSON: !Ref KeyJSON
ValueJSON: !Ref ValueJSON
The problem is when there is an error, the Cloudformation stack gets stuck in a state like UPDATE_IN_PROGRESS etc.
How do I handle failures in such scenarios?
My goal is to set up some lambda functions which are public (i.e. no authorization required to send requests) and other ones which require a User to be logged in within a Cognito UserPool.
In my CDK file below, I'm adding an Authorizer only on one of the two endpoints, but then when I launch a request both of them are unprotected, and in the function logs you can see there is no Cognito UserPool nor AuthenticationType.
Any ideas on what's missing?
Thanks!
{
"httpMethod":"GET",
"body":null,
"resource":"/private",
"requestContext":{
...,
"identity":{
"apiKey":null,
"userArn":null,
"cognitoAuthenticationType":null,
"caller":null,
"userAgent":"Custom User Agent String",
"user":null,
"cognitoIdentityPoolId":null,
"cognitoAuthenticationProvider":null,
"sourceIp":"127.0.0.1",
"accountId":null
},
...
},
...
}
CDK file:
import * as apigateway from '#aws-cdk/aws-apigateway';
import * as lambda from '#aws-cdk/aws-lambda';
import * as s3 from '#aws-cdk/aws-s3';
import { UserPool, VerificationEmailStyle, UserPoolClient } from '#aws-cdk/aws-cognito'
import { App, CfnParameter, Duration, Stack, StackProps } from '#aws-cdk/core';
export class CdkStack extends Stack {
constructor(scope: App, id: string, props: StackProps) {
super(scope, id, props);
new CfnParameter(this, 'AppId');
const userPool = new UserPool(this, 'dev-users', {
userPoolName: 'dev-users',
selfSignUpEnabled: true,
userVerification: {
emailSubject: 'Verify your email for our awesome app!',
emailBody: 'Hello {username}, Thanks for signing up to our awesome app! Your verification code is {####}',
emailStyle: VerificationEmailStyle.CODE,
smsMessage: 'Hello {username}, Thanks for signing up to our awesome app! Your verification code is {####}',
},
signInAliases: {
email: true
},
signInCaseSensitive: false,
standardAttributes: {
email: { required: true, mutable: false }
},
passwordPolicy: {
minLength: 6,
requireLowercase: true,
requireUppercase: true,
requireDigits: true,
requireSymbols: false,
tempPasswordValidity: Duration.days(7),
}
})
const environment = { };
// The code will be uploaded to this location during the pipeline's build step
const artifactBucket = s3.Bucket.fromBucketName(this, 'ArtifactBucket', process.env.S3_BUCKET!);
const artifactKey = `${process.env.CODEBUILD_BUILD_ID}/function-code.zip`;
const code = lambda.Code.fromBucket(artifactBucket, artifactKey);
// This is a Lambda function config associated with the source code: get-all-items.js
const publicFunction = new lambda.Function(this, 'publicFunction', {
description: 'A simple example includes a HTTP get method accessible to everyone',
handler: 'src/handlers/public.publicHandler',
runtime: lambda.Runtime.NODEJS_10_X,
code,
environment,
timeout: Duration.seconds(60),
});
// Give Read permissions to the SampleTable
// This is a Lambda function config associated with the source code: put-item.js
const privateFunction = new lambda.Function(this, 'privateFunction', {
description: 'This functions should only be accessible to authorized users from a Cognito UserPool',
handler: 'src/handlers/private.privateHandler',
runtime: lambda.Runtime.NODEJS_10_X,
code,
timeout: Duration.seconds(60),
environment,
});
const api = new apigateway.RestApi(this, 'ServerlessRestApi', { cloudWatchRole: false });
const authorizer = new apigateway.CfnAuthorizer(this, 'cfnAuth', {
restApiId: api.restApiId,
name: 'HelloWorldAPIAuthorizer',
type: 'COGNITO_USER_POOLS',
identitySource: 'method.request.header.Authorization',
providerArns: [userPool.userPoolArn],
})
api.root.addResource('public').addMethod(
'GET',
new apigateway.LambdaIntegration(publicFunction)
);
api.root.addResource('private').addMethod(
'GET',
new apigateway.LambdaIntegration(privateFunction),
{
authorizationType: apigateway.AuthorizationType.COGNITO,
authorizer: {
authorizerId: authorizer.ref
}
}
);
}
}
const app = new App();
new CdkStack(app, 'CognitoProtectedApi', {});
app.synth();
Try doing the following in your addMethod.
{
authorizationType: apigateway.AuthorizationType.COGNITO,
authorizer // pass the authorizer object instead of authorizerId stuff.
}
Refer https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.CognitoUserPoolsAuthorizer.html for more details.
Following is for AWS CDK 2.20.0
You can create a CognitoUserPoolsAuthorizer and then either attach it as default authorizer for an API GW, or attach it specific route.
For adding to a specific method,
const userPool = new cognito.UserPool(this, 'UserPool');
const auth = new apigateway.CognitoUserPoolsAuthorizer(this, 'booksAuthorizer', {
cognitoUserPools: [userPool]
});
declare const books: apigateway.Resource;
books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
authorizer: auth,
authorizationType: apigateway.AuthorizationType.COGNITO,
})
Refer https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.CognitoUserPoolsAuthorizer.html