Skip to content

Commit

Permalink
Merge pull request #1388 from akkumaha/akkumaha-feature-lambda-s3-pol…
Browse files Browse the repository at this point in the history
…ly-cdk

New Serverless Pattern - Lambda-S3-Polly-CDK
  • Loading branch information
Ben Smith committed Jun 20, 2023
2 parents 48b0600 + 53bd408 commit 0b248b8
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 0 deletions.
40 changes: 40 additions & 0 deletions lambda-s3-polly-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# S3 Triggered Lambda Function that starts an Amazon Polly Task and points to another S3 bucket for results

This repo contains serverless patterns showing how to setup a Lambda with an S3 *object created* trigger that starts a basic, Amazon Polly Task. The resulting speech is placed into another S3 bucket.

![Demo Project Solution Architecture Diagram](architecture.png)

- Learn more about these patterns at https://serverlessland.com/patterns.
- To learn more about submitting a pattern, read the [publishing guidelines page](https://github.com/aws-samples/serverless-patterns/blob/main/PUBLISHING.md).

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* AWS Account
* AWS CLI already configured with Administrator permission
* [NodeJS 14.x installed](https://nodejs.org/en/download/)
* CDK v2 installed: See Getting Started With the AWS CDK
* Python CDK required libraries: (install with pip install -r requirements.txt)
* Clone this repo!

## Deployment Instructions

1. Within your CDK Python module directory(where all your cdk stacks are located) create a constructs folder and within the constructs folder, create an assets folder.
2. Place the `amazon-polly-stack.py` file in the constructs folder you created and the `file-uploaded-trigger` folder in the assets folder you created.
3. In your terminal run `CDK Deploy` for the specified stack that uses this construct

### Removing the resources

1. run `CDK Destroy <stack id>` for the specified stack that used this construct

```
git clone https://github.com/aws-samples/serverless-patterns/lambda-s3-polly-cdk
```

Each subdirectory contains additional installation and usage instructions.

----
Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
----

28 changes: 28 additions & 0 deletions lambda-s3-polly-cdk/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from pattern.amazon_polly_stack import AmazonPollyStack


app = cdk.App()
AmazonPollyStack(app, "AmazonPollyStack",
# If you don't specify 'env', this stack will be environment-agnostic.
# Account/Region-dependent features and context lookups will not work,
# but a single synthesized template can be deployed anywhere.

# Uncomment the next line to specialize this stack for the AWS Account
# and Region that are implied by the current CLI configuration.

#env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),

# Uncomment the next line if you know exactly what Account and Region you
# want to deploy the stack to. */

#env=cdk.Environment(account='123456789012', region='us-east-1'),

# For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
)

app.synth()
Binary file added lambda-s3-polly-cdk/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions lambda-s3-polly-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true
}
}
57 changes: 57 additions & 0 deletions lambda-s3-polly-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"title": "S3 to Lambda to Polly",
"description": "Create a Lambda with an S3 object created trigger that starts an Amazon Polly Task and puts the results in anoter S3 Bucket",
"language": "Python",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern contains a sample AWS Cloud Development Kit (AWS CDK) template that deploys a Lambda Function with an S3 object created trigger to start an Amazon Polly Task and place the results in another S3 bucket.",
"This pattern deploys one Lambda Function and two S3 Buckets."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-s3-polly-cdk",
"templateURL": "serverless-patterns/lambda-s3-polly-cdk",
"projectFolder": "pattern",
"templateFile": "assets/lambda-s3-polly-cdk.py"
}
},
"resources": {
"bullets": [
{
"text": "Using an Amazon S3 trigger to invoke a Lambda function",
"link": "https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html"
},
{
"text": "Guidelines and quotas - Amazon Polly",
"link": "https://docs.aws.amazon.com/polly/latest/dg/limits.html"
}
]
},
"deploy": {
"text": [
"cdk deploy"
]
},
"testing": {
"text": [
"See the Github repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: <code>cdk delete</code>."
]
},
"authors": [
{
"name": "Kranthi Kiran A",
"image": "https://media.licdn.com/dms/image/C5603AQGMzL2iVESvpA/profile-displayphoto-shrink_400_400/0/1662286284377?e=1691020800&v=beta&t=p7litfMASeNBNU-Xeb7bpl5WD8KoWrlVd3Azl3_q1bg",
"bio": "Kranthi is an Associate Cloud Developer with Amazon Web Services",
"linkedin": "https://www.linkedin.com/in/akkiran003/"
}
]
}
62 changes: 62 additions & 0 deletions lambda-s3-polly-cdk/pattern/amazon_polly_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from aws_cdk import (
# Duration,
Stack,
aws_s3 as s3,
aws_lambda as lambda_,
aws_iam as iam,
Duration,
aws_lambda_event_sources as eventsources
)
from constructs import Construct

class AmazonPollyStack(Stack):

def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

#Input Bucket
self.input_bucket = s3.Bucket(self, "input-bucket",
versioned=True,
encryption=s3.BucketEncryption.S3_MANAGED,
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
enforce_ssl=True)

# Destination Bucket
self.destination_bucket = s3.Bucket(self, 'destination-bucket',
versioned=True,
encryption=s3.BucketEncryption.S3_MANAGED,
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
enforce_ssl=True)

python_lambda_kwargs = {
'handler': 'lambda_function.lambda_handler',
'runtime': lambda_.Runtime.PYTHON_3_9,
'timeout': Duration.minutes(2)
}

# Trigger Transcription Lambda
trigger_polly_lambda = lambda_.Function(self, "file-upload-trigger", **python_lambda_kwargs,
code=lambda_.Code.from_asset(
'./pattern/assets'),
function_name="start-polly",
initial_policy=[
iam.PolicyStatement(
actions=['polly:SynthesizeSpeech'],
resources=['*']),
iam.PolicyStatement(actions=['s3:PutObject'],
resources=[
self.destination_bucket.bucket_arn+'/*']
),
iam.PolicyStatement(actions=['s3:GetObject',
's3:ListBucket'],
resources=[
self.input_bucket.bucket_arn,
self.input_bucket.bucket_arn + '/*'
]
)
]
)

# Add Trigger and Environment Variable
trigger_polly_lambda.add_event_source(eventsources.S3EventSource(self.input_bucket, events=[s3.EventType.OBJECT_CREATED]))
trigger_polly_lambda.add_environment('DESTINATION_BUCKET', self.destination_bucket.bucket_name)
47 changes: 47 additions & 0 deletions lambda-s3-polly-cdk/pattern/assets/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json
import boto3
import os
from tempfile import gettempdir
from contextlib import closing

# Get the s3 client
s3 = boto3.client("s3")
# Create a client to use Polly
polly_client = boto3.client("polly")

output_bucket = os.environ['DESTINATION_BUCKET']

def lambda_handler(event, context):

# Get the s3 bucket name
bucket_name = event["Records"][0]["s3"]["bucket"]["name"]
# Get the file name
file_name = event["Records"][0]["s3"]["object"]["key"]
# Get the file object
file_obj = s3.get_object(Bucket=bucket_name, Key=file_name)
# Read the file
file_content = file_obj["Body"].read().decode("utf-8")

# Call the Polly API to convert the text to speech
response = polly_client.synthesize_speech(
Text=file_content,
OutputFormat="mp3",
VoiceId="Joanna"
)
print(response)
# Get the audio file
audio_file = response["AudioStream"]
# Save the audio file to the s3 bucket
audio_file_name = file_name.split(".")[0] + ".mp3"
print(audio_file_name)
with closing(audio_file) as stream:
s3.put_object(Key=audio_file_name, Body=stream.read(), Bucket=output_bucket)
##s3.put_object(Bucket=bucket_name, Key=audio_file_name, Body=file)
##with open(audio_file, 'rb') as data:
## s3.upload_fileobj(data, bucket_name, audio_file_name)
return {
"statusCode": 200,
"body": json.dumps({
"message": "Converted the text to speech"
}),
}
2 changes: 2 additions & 0 deletions lambda-s3-polly-cdk/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.70.0
constructs>=10.0.0,<11.0.0

0 comments on commit 0b248b8

Please sign in to comment.