Adding eventSource to Lambda by ARN in CDK - amazon-web-services

I have a lambda function which reads from Dynamodb stream. I have the Dynamodb stream ARN exported from another stack in the same AWS account. Now, while adding eventSource in Lambda, it asks from Table construct.
const function = new lambda.Function(...);
function.addEventSource(new DynamoEventSource(table, {
startingPosition: lambda.StartingPosition.TrimHorizon
}));
Ref: https://awslabs.github.io/aws-cdk/refs/_aws-cdk_aws-lambda-event-sources.html#dynamodb-streams
But I have the stream ARN. Is there any way I can make use of this to add the event source. Or I have to export the table itself?

It's currently not possible to import a DynamoDB table with the AWS CDK. Importing a DynamoDB table.
Still you can reach your goal by using the EventSourceMapping class from #aws-cdk/aws-lambda directly:
import iam = require('#aws-cdk/aws-iam');
import lambda = require('#aws-cdk/aws-lambda');
const fn = new lambda.Function(...);
new lambda.EventSourceMapping(this, 'DynamoDBEventSource', {
target: fn,
batchSize: ...,
eventSourceArn: <your stream arn>,
startingPosition: lambda.StartingPosition.TrimHorizon
});
fn.addToRolePolicy(
new iam.PolicyStatement()
.addActions('dynamodb:DescribeStream', 'dynamodb:GetRecords', 'dynamodb:GetShardIterator', 'dynamodb:ListStreams')
.addResource('<your stream arn>/*');
);

As the Stream ARN could change every time it's disabled/enabled, one can look up the ARN dynamically and export it as env variable.
export STREAM_ARN=$(aws dynamodb describe-table --table-name MyFirstTable |jq -r '.Table.LatestStreamArn')
Then this could be used inside CDK Code
const table = dynamodb.Table.fromTableAttributes(this, 'id-for-table', {
tableArn:
"arn:aws:dynamodb:us-east-1:123456789:table/MyFirstTable",
tableStreamArn:process.env.STREAM_ARN
});

Related

How to Reuse the api gate TokenAuthorizer

I want to reuse the TokenAuthorizer which I have created in another stack. If a do the below it gives an error that it already exists, and if I change the authorizerName it creates a new one.
Is there a way I can reuse this resource?
const authzHandler = lambda.Function.fromFunctionName(this, 'AuthHandlerLookup', 'auth-handler');
const authorizer = new apigateway.TokenAuthorizer(this, 'WebApiTokenAuthorizer', {
handler: authzHandler,
resultsCacheTtl: Duration.seconds(600),
authorizerName: 'test-Authorizer',
assumeRole: lambdaExecutionRole
});
test.addMethod('GET', new apigateway.LambdaIntegration(TestLambda , { proxy: true }),
{
authorizer
}
i am able to get the authorizer information in cli , but now sure how to do the same using cdk
aws apigateway get-authorizer --rest-api-id wrrt25mzme0m --authorizer-id vffawds

AWS CDK - compile-time programmatic extraction of account ID from ARN string

I have the ARN of a downstream resource of an external AWS account. My infrastructure code is in AWS CDK. In my code, I want to extract the accountId from the ARN. How do I do that?
It can be elegantly done using the core cdk library. Here's the solution:
import { Arn } from 'monocdk';
import arn = require("monocdk/lib/core/lib/arn");
private static getAccountIdFromArn(arn: string): string {
const arnComponents = Arn.parse(arn)
if(undefined === arnComponents.account) {
throw new Error(`account id not present in the arn #{arn}!`)
}
return arnComponents.account
}

How to get the AWS IoT custom endpoint in CDK?

I want to pass the IoT custom endpoint as an env var to a lambda declared in CDK.
I'm talking about the IoT custom endpoint that lives here:
How do I get it in context of CDK?
You can ref AWS sample code:
https://github.com/aws-samples/aws-iot-cqrs-example/blob/master/lib/querycommandcontainers.ts
const getIoTEndpoint = new customResource.AwsCustomResource(this, 'IoTEndpoint', {
onCreate: {
service: 'Iot',
action: 'describeEndpoint',
physicalResourceId: customResource.PhysicalResourceId.fromResponse('endpointAddress'),
parameters: {
"endpointType": "iot:Data-ATS"
}
},
policy: customResource.AwsCustomResourcePolicy.fromSdkCalls({resources: customResource.AwsCustomResourcePolicy.ANY_RESOURCE})
});
const IOT_ENDPOINT = getIoTEndpoint.getResponseField('endpointAddress')
AFAIK the only way to recover is by using Custom Resources (Lambda), for example (IoTThing): https://aws.amazon.com/blogs/iot/automating-aws-iot-greengrass-setup-with-aws-cloudformation/

Set up S3 Bucket level Events using AWS CloudFormation

I am trying to get AWS CloudFormation to create a template that will allow me to attach an event to an existing S3 Bucket that will trigger a Lambda Function whenever a new file is put into a specific directory within the bucket. I am using the following YAML as a base for the CloudFormation template but cannot get it working.
---
AWSTemplateFormatVersion: '2010-09-09'
Resources:
SETRULE:
Type: AWS::S3::Bucket
Properties:
BucketName: bucket-name
NotificationConfiguration:
LambdaConfigurations:
- Event: s3:ObjectCreated:Put
Filter:
S3Key:
Rules:
- Name: prefix
Value: directory/in/bucket
Function: arn:aws:lambda:us-east-1:XXXXXXXXXX:function:lambda-function-trigger
Input: '{ CONFIGS_INPUT }'
I have tried rewriting this template a number of different ways to no success.
Since you have mentioned that those buckets already exists, this is not going to work. You can use CloudFormation in this way but only to create a new bucket, not to modify existing bucket if that bucket was not created via that template in the first place.
If you don't want to recreate your infrastructure, it might be easier to just use some script that will subscribe lambda function to each of the buckets. As long as you have a list of buckets and the lambda function, you are ready to go.
Here is a script in Python3. Assuming that we have:
2 buckets called test-bucket-jkg2 and test-bucket-x1gf
lambda function with arn: arn:aws:lambda:us-east-1:605189564693:function:my_func
There are 2 steps to make this work. First, you need to add function policy that will allow s3 service to execute that function. Second, you will loop through the buckets one by one, subscribing lambda function to each one of them.
import boto3
s3_client = boto3.client("s3")
lambda_client = boto3.client('lambda')
buckets = ["test-bucket-jkg2", "test-bucket-x1gf"]
lambda_function_arn = "arn:aws:lambda:us-east-1:605189564693:function:my_func"
# create a function policy that will permit s3 service to
# execute this lambda function
# note that you should specify SourceAccount and SourceArn to limit who (which account/bucket) can
# execute this function - you will need to loop through the buckets to achieve
# this, at least you should specify SourceAccount
try:
response = lambda_client.add_permission(
FunctionName=lambda_function_arn,
StatementId="allow s3 to execute this function",
Action='lambda:InvokeFunction',
Principal='s3.amazonaws.com'
# SourceAccount="your account",
# SourceArn="bucket's arn"
)
print(response)
except Exception as e:
print(e)
# loop through all buckets and subscribe lambda function
# to each one of them
for bucket in buckets:
print("putting config to bucket: ", bucket)
try:
response = s3_client.put_bucket_notification_configuration(
Bucket=bucket,
NotificationConfiguration={
'LambdaFunctionConfigurations': [
{
'LambdaFunctionArn': lambda_function_arn,
'Events': [
's3:ObjectCreated:*'
]
}
]
}
)
print(response)
except Exception as e:
print(e)
You could write a custom resource to do this, in fact that's what I've ended up doing at work for the same problem. At the simplest level, define a lambda that takes a put bucket notification configuration and then just calls the put bucket notification api with the data that was passed it.
If you want to be able to control different notifications across different cloudformation templates, then it's a bit more complex. Your custom resource lambda will need to read the existing notifications from S3 and then update these based on what data was passed to it from CF.

Latest Lambda Layer ARN

I have a lambda layer which I keep updating. This lambda layer has multiple versions. How can I find the lambda layer ARN with latest version using aws cli?
I am able to do this using the command listed below -
aws lambda list-layer-versions --layer-name <layer name> --region us-east-1 --query 'LayerVersions[0].LayerVersionArn'
Unfortunately, it's currently not possible (I have encountered the same issue).
You can keep the latest ARN in your own place (like DynamoDB) and update it whenever you publish a new version of the layer.
You can create a custom macro to get the latest lambda layer version and use that as a reference.
The following function gets the latest version from the Lambda Layer stack:
import json
import boto3
def latest_lambdalayer(event, context):
fragment = get_latestversion(event['fragment'])
return {
'requestId': event['requestId'],
'status': 'success',
'fragment': fragment
}
def get_latestversion(fragment):
cloudformation = boto3.resource('cloudformation')
stack = cloudformation.Stack('ticketapp-layer-dependencies')
for o in stack.outputs:
if o['OutputKey']=='TicketAppLambdaDependency':
return o['OutputValue']
#return "arn:aws:lambda:eu-central-1:899885580749:layer:ticketapp-dependencies-layer:16"
And you use this when defining the Lambda layer—here using same global template:
Globals:
Function:
Layers:
- !Transform { "Name" : "LatestLambdaLayer"}
Runtime: nodejs12.x
MemorySize: 128
Timeout: 101