{"id":18573132,"url":"https://github.com/localstack-samples/sample-serverless-image-resizer-s3-lambda","last_synced_at":"2025-04-10T07:31:49.110Z","repository":{"id":68733075,"uuid":"569498519","full_name":"localstack-samples/sample-serverless-image-resizer-s3-lambda","owner":"localstack-samples","description":"Serverless Image Resizer demo app for LocalStack","archived":false,"fork":false,"pushed_at":"2025-01-24T15:04:38.000Z","size":120,"stargazers_count":27,"open_issues_count":2,"forks_count":16,"subscribers_count":16,"default_branch":"main","last_synced_at":"2025-03-24T18:06:28.869Z","etag":null,"topics":["aws","developer-hub","github-actions","lambda","localstack","localstack-developer-hub","pytest","s3","serverless","ses","sns"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/localstack-samples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-23T00:50:02.000Z","updated_at":"2025-03-24T12:53:56.000Z","dependencies_parsed_at":"2023-12-02T01:28:52.704Z","dependency_job_id":"608a088f-4ed4-4fc9-b8bb-87d76cc7aa94","html_url":"https://github.com/localstack-samples/sample-serverless-image-resizer-s3-lambda","commit_stats":null,"previous_names":["localstack/sample-serverless-image-resizer-s3-lambda","localstack-samples/sample-serverless-image-resizer-s3-lambda"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localstack-samples%2Fsample-serverless-image-resizer-s3-lambda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localstack-samples%2Fsample-serverless-image-resizer-s3-lambda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localstack-samples%2Fsample-serverless-image-resizer-s3-lambda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localstack-samples%2Fsample-serverless-image-resizer-s3-lambda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/localstack-samples","download_url":"https://codeload.github.com/localstack-samples/sample-serverless-image-resizer-s3-lambda/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248176384,"owners_count":21060058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","developer-hub","github-actions","lambda","localstack","localstack-developer-hub","pytest","s3","serverless","ses","sns"],"created_at":"2024-11-06T23:08:07.546Z","updated_at":"2025-04-10T07:31:48.826Z","avatar_url":"https://github.com/localstack-samples.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Serverless image resizer\n\n[![LocalStack Pods Launchpad](https://localstack.cloud/gh/launch-pod-badge.svg)](https://app.localstack.cloud/launchpad?url=https://github.com/localstack/sample-serverless-image-resizer-s3-lambda/releases/download/latest/release-pod.zip)\n[![GitHub Actions](https://github.com/localstack-samples/sample-serverless-image-resizer-s3-lambda/actions/workflows/integration-test.yml/badge.svg)](https://github.com/localstack-samples/sample-serverless-image-resizer-s3-lambda/actions/workflows/integration-test.yml)\n[![CircleCI](https://dl.circleci.com/status-badge/img/gh/localstack-samples/sample-serverless-image-resizer-s3-lambda/tree/main.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/localstack-samples/sample-serverless-image-resizer-s3-lambda/tree/main)\n[![AWS CodeBuild](https://codebuild.eu-central-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiUUVDZExZT0ovUm5YejlKcHlXeGpuT1pRaC9hSzdDcFJVYlMvZzl0emtnWW5qcVdQUGY3YlJYWnBJeEdoOHd0OVkvd29XTUxuL2p3d3hVS0NFRnFlTzhRPSIsIml2UGFyYW1ldGVyU3BlYyI6IjQrdHhqVGY1N24ycTEvQkMiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D\u0026branch=codebuild-sample)](https://eu-central-1.codebuild.aws.amazon.com/project/eyJlbmNyeXB0ZWREYXRhIjoiOGpTQnR0a0J6ZnYwN3hNQ21DVkFoUU8zWTc4TExSaGk0b2p5UkVyNWhHSXhLSWZUSWt3eE1PUnpLZTRMWld2U3l3bVBWa2Frc084YjJ6UFZDRjNlcTc0U0xOa2lqVU1qZXdJMUFzdEVudz09IiwiaXZQYXJhbWV0ZXJTcGVjIjoib1FaZmhKMHZkc0NTbmdqcSIsIm1hdGVyaWFsU2V0U2VyaWFsIjoxfQ%3D%3D)\n[![GitLabCI](https://gitlab.com/localstack.cloud/samples/sample-serverless-image-resizer-s3-lambda/badges/main/pipeline.svg?ignore_skipped=true)](https://gitlab.com/localstack.cloud/samples/sample-serverless-image-resizer-s3-lambda)\n\n| Key          | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Environment  | \u003cimg src=\"https://img.shields.io/badge/LocalStack-deploys-4D29B4.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKgAAACoABZrFArwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALbSURBVHic7ZpNaxNRFIafczNTGIq0G2M7pXWRlRv3Lusf8AMFEQT3guDWhX9BcC/uFAr1B4igLgSF4EYDtsuQ3M5GYrTaj3Tmui2SpMnM3PlK3m1uzjnPw8xw50MoaNrttl+r1e4CNRv1jTG/+v3+c8dG8TSilHoAPLZVX0RYWlraUbYaJI2IuLZ7KKUWCisgq8wF5D1A3rF+EQyCYPHo6Ghh3BrP8wb1en3f9izDYlVAp9O5EkXRB8dxxl7QBoNBpLW+7fv+a5vzDIvVU0BELhpjJrmaK2NMw+YsIxunUaTZbLrdbveZ1vpmGvWyTOJToNlsuqurq1vAdWPMeSDzwzhJEh0Bp+FTmifzxBZQBXiIKaAq8BBDQJXgYUoBVYOHKQRUER4mFFBVeJhAQJXh4QwBVYeHMQJmAR5GCJgVeBgiYJbg4T8BswYPp+4GW63WwvLy8hZwLcd5TudvBj3+OFBIeA4PD596nvc1iiIrD21qtdr+ysrKR8cY42itCwUP0Gg0+sC27T5qb2/vMunB/0ipTmZxfN//orW+BCwmrGV6vd63BP9P2j9WxGbxbrd7B3g14fLfwFsROUlzBmNM33XdR6Meuxfp5eg54IYxJvXCx8fHL4F3w36blTdDI4/0WREwMnMBeQ+Qd+YC8h4g78wF5D1A3rEqwBiT6q4ubpRSI+ewuhP0PO/NwcHBExHJZZ8PICI/e73ep7z6zzNPwWP1djhuOp3OfRG5kLROFEXv19fXP49bU6TbYQDa7XZDRF6kUUtEtoFb49YUbh/gOM7YbwqnyG4URQ/PWlQ4ASllNwzDzY2NDX3WwioKmBgeqidgKnioloCp4aE6AmLBQzUExIaH8gtIBA/lFrCTFB7KK2AnDMOrSeGhnAJSg4fyCUgVHsolIHV4KI8AK/BQDgHW4KH4AqzCQwEfiIRheKKUAvjuuu7m2tpakPdMmcYYI1rre0EQ1LPo9w82qyNziMdZ3AAAAABJRU5ErkJggg==\"\u003e |\n| Services     | S3, SSM, Lambda, SNS, SES                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\n| Integrations | AWS SDK, AWS CLI, GitHub actions, pytest                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| Categories   | Serverless, S3 notifications, S3 website, Lambda function URLs, LocalStack developer endpoints, JavaScript, Python                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| Level        | Intermediate                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n\n## Introduction\n\nThis is an app to resize images uploaded to S3 in a serverless way.\nA simple web fronted using HTML and JavaScript provides a way for users to upload images that are resized and listed. \nWe use a Lambda to generate S3 pre-signed URLs so the upload form can upload directly to S3 rather than going through the Lambda.\nS3 bucket notifications are used to trigger a Python Lambda that runs image resizing.\nAnother Lambda is used to list all uploaded and resized images, and provide pre-signed URLs for the browser to display them.\nWe also demonstrate how Lambda failures can submit to SNS, which can then trigger an SES email.\nUsing the LocalStack internal `/_localstack/aws/ses` endpoint, we can run end-to-end integration tests to verify that emails have been sent correctly.\n\nHere's a short summary of AWS service features we use:\n* S3 bucket notifications to trigger a Lambda\n* S3 pre-signed POST\n* S3 website\n* Lambda function URLs\n* Lambda SNS on failure destination\n* SNS to SES Subscriptions\n* SES LocalStack testing endpoint\n\nHere's the web application in action:\n\nhttps://user-images.githubusercontent.com/3996682/229314248-86122e9e-0150-4292-b889-401e6fb8f398.mp4\n\nMoreover, the repo includes a GitHub actions workflow to demonstrate how to run end-to-end tests of your AWS apps using LocalStack in CI.\nThe GitHub workflow runs a set of integration tests using pytest.\n\n## Architecture overview\n\n![Screenshot at 2023-04-02 01-32-56](https://user-images.githubusercontent.com/3996682/229322761-92f52eec-5bfb-412a-a3cb-8af4ee1fed24.png)\n\n## Prerequisites\n\n### Dev environment\n\nMake sure you use the same version as the Python Lambdas to make Pillow work.\nIf you use pyenv, then first install and activate Python 3.11:\n\n```bash\npyenv install 3.11.6\npyenv global 3.11.6\n```\n\n```console\n% python --version\nPython 3.11.6\n```\n\nCreate a virtualenv and install all the development dependencies there:\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -r requirements-dev.txt\n```\n\n## Instructions\n\nYou can set up and deploy the sample application on LocalStack by executing the commands in our Makefile. First, create a `.env` file using the provided `.env.example` file as a template, and include your LocalStack token in it. Then, run `make start` to initiate LocalStack on your machine. \n\nNext, execute `make install` to install needed dependencies.\n\nAfter that, launch `make terraform-setup` to provision the infrastructure on LocalStack using Terraform CLI and its scripts. Alternatively, run `make awslocal-setup` to set up the infrastructure using `awslocal`, a wrapper for the AWS CLI.\n\nIf you prefer, you can also follow these step-by-step instructions for a manual deployment.\n\n### LocalStack\n\nStart LocalStack Pro with Auth Token:\n\n```bash\nLOCALSTACK_AUTH_TOKEN=... localstack start (-d)\n```\n\n### Terraform\n\nTo create the infrastructure using Terraform, run the following commands:\n\n```shell\ncd deployment/terraform\ntflocal init\ntflocal apply --auto-approve\n```\n\nWe are using the `tflocal` wrapper to configure the local service endpoints, and send the API requests to LocalStack, instead of AWS.\n\n### AWS CLI\n\nYou can execute the following commands to set up the infrastructure using `awslocal`. All the commands are also available in the `deployment/awslocal/deploy.sh` script.\n\n#### Create the buckets\n\nThe names are completely configurable via SSM:\n\n```bash\nawslocal s3 mb s3://localstack-thumbnails-app-images\nawslocal s3 mb s3://localstack-thumbnails-app-resized\n```\n\n#### Put the bucket names into the parameter store\n\n```bash\nawslocal ssm put-parameter --name /localstack-thumbnail-app/buckets/images --type \"String\" --value \"localstack-thumbnails-app-images\"\nawslocal ssm put-parameter --name /localstack-thumbnail-app/buckets/resized --type \"String\" --value \"localstack-thumbnails-app-resized\"\n```\n\n#### Create the DLQ Topic for failed lambda invokes\n\n```bash\nawslocal sns create-topic --name failed-resize-topic\n```\n\nSubscribe an email address to it (to alert us immediately if an image resize fails!).\n\n```bash\nawslocal sns subscribe \\\n    --topic-arn arn:aws:sns:us-east-1:000000000000:failed-resize-topic \\\n    --protocol email \\\n    --notification-endpoint my-email@example.com\n```\n\n#### Create the lambdas\n\n##### S3 pre-signed POST URL generator\n\nThis Lambda is responsible for generating pre-signed POST URLs to upload files to an S3 bucket.\n\n```bash\n(cd lambdas/presign; rm -f lambda.zip; zip lambda.zip handler.py)\nawslocal lambda create-function \\\n    --function-name presign \\\n    --runtime python3.11 \\\n    --timeout 10 \\\n    --zip-file fileb://lambdas/presign/lambda.zip \\\n    --handler handler.handler \\\n    --role arn:aws:iam::000000000000:role/lambda-role \\\n    --environment Variables=\"{STAGE=local}\"\n```\n\nCreate the function URL:\n\n```bash\nawslocal lambda create-function-url-config \\\n    --function-name presign \\\n    --auth-type NONE\n```\n\nCopy the `FunctionUrl` from the response, you will need it later to make the app work.\n\n#### Image lister lambda\n\nThe `list` Lambda is very similar:\n\n```bash\n(cd lambdas/list; rm -f lambda.zip; zip lambda.zip handler.py)\nawslocal lambda create-function \\\n    --function-name list \\\n    --handler handler.handler \\\n    --zip-file fileb://lambdas/list/lambda.zip \\\n    --runtime python3.11 \\\n    --role arn:aws:iam::000000000000:role/lambda-role \\\n    --environment Variables=\"{STAGE=local}\"\n```\n\nCreate the function URL:\n\n```bash\nawslocal lambda create-function-url-config \\\n    --function-name list \\\n    --auth-type NONE\n```\n\n#### Resizer Lambda\n\n```bash\n(\n    cd lambdas/resize\n    rm -rf package lambda.zip\n    mkdir package\n    pip install -r requirements.txt -t package --platform manylinux2014_x86_64 --only-binary=:all:\n    zip lambda.zip handler.py\n    cd package\n    zip -r ../lambda.zip *;\n)\nawslocal lambda create-function \\\n    --function-name resize \\\n    --runtime python3.11 \\\n    --timeout 10 \\\n    --zip-file fileb://lambdas/resize/lambda.zip \\\n    --handler handler.handler \\\n    --dead-letter-config TargetArn=arn:aws:sns:us-east-1:000000000000:failed-resize-topic \\\n    --role arn:aws:iam::000000000000:role/lambda-role \\\n    --environment Variables=\"{STAGE=local}\"\n```\n\n#### Connect the S3 bucket to the resizer lambda\n\n```bash\nawslocal s3api put-bucket-notification-configuration \\\n    --bucket localstack-thumbnails-app-images \\\n    --notification-configuration \"{\\\"LambdaFunctionConfigurations\\\": [{\\\"LambdaFunctionArn\\\": \\\"$(awslocal lambda get-function --function-name resize | jq -r .Configuration.FunctionArn)\\\", \\\"Events\\\": [\\\"s3:ObjectCreated:*\\\"]}]}\"\n```\n\n#### Create the static s3 webapp\n\n```bash\nawslocal s3 mb s3://webapp\nawslocal s3 sync --delete ./website s3://webapp\nawslocal s3 website s3://webapp --index-document index.html\n```\n\n#### Using the application\n\nOnce deployed, visit http://webapp.s3-website.localhost.localstack.cloud:4566\n\nPaste the Function URL of the presign Lambda you created earlier into the form field.\n```bash\nawslocal lambda list-function-url-configs --function-name presign\nawslocal lambda list-function-url-configs --function-name list\n```\n\nAfter uploading a file, you can download the resized file from the `localstack-thumbnails-app-resized` bucket.\n\n### Testing failures\n\nIf the `resize` Lambda fails, an SNS message is sent to a topic that an SES subscription listens to.\nAn email is then sent with the raw failure message.\nIn a real scenario you'd probably have another lambda sitting here, but it's just for demo purposes.\nSince there's no real email server involved, you can use the [SES developer endpoint](https://docs.localstack.cloud/user-guide/aws/ses/) to list messages that were sent via SES:\n\n```bash\ncurl -s http://localhost.localstack.cloud:4566/_aws/ses\n```\n\nAn alternative is to use a service like MailHog or smtp4dev, and start LocalStack using `SMTP_HOST=host.docker.internal:1025` pointing to the mock SMTP server.\n\n### Run integration tests\n\nOnce all resource are created on LocalStack, you can run the automated integration tests.\n\n```bash\npytest tests/\n```\n\n### GitHub Action\n\nThe demo LocalStack in CI, `.github/workflows/integration-test.yml` contains a GitHub Action that starts up LocalStack,\ndeploys the infrastructure to it, and then runs the integration tests.\n\n## Contributing\n\nWe appreciate your interest in contributing to our project and are always looking for new ways to improve the developer experience. We welcome feedback, bug reports, and even feature ideas from the community.\nPlease refer to the [contributing file](CONTRIBUTING.md) for more details on how to get started. \n\n## Cloud Pods\n\n[Cloud Pods](https://docs.localstack.cloud/user-guide/tools/cloud-pods/) are a mechanism that allows you to take a snapshot of the state in your current LocalStack instance, persist it to a storage backend, and easily share it with your team members.\n\nYou can convert your current AWS infrastructure state to a Cloud Pod using the `localstack` CLI. \nCheck out our [Getting Started guide](https://docs.localstack.cloud/user-guide/cloud-pods/getting-started/) and [LocalStack Cloud Pods CLI reference](https://docs.localstack.cloud/user-guide/cloud-pods/pods-cli/) to learn more about Cloud Pods and how to use them.\n\nTo inject a Cloud Pod you can use [Cloud Pods Launchpad](https://docs.localstack.cloud/user-guide/cloud-pods/launchpad/) wich quickly injects Cloud Pods into your running LocalStack container. \n\nClick here [![LocalStack Pods Launchpad](https://localstack.cloud/gh/launch-pod-badge.svg)](https://app.localstack.cloud/launchpad?url=https://github.com/localstack/sample-serverless-image-resizer-s3-lambda/releases/download/latest/release-pod.zip) to launch the Cloud Pods Launchpad and inject the Cloud Pod for this application by clicking the `Inject` button.\n\n\nAlternatively, you can inject the pod by using the `localstack` CLI. \nFirst, you need to download the pod you want to inject from the [releases](https://github.com/localstack/sample-serverless-image-resizer-s3-lambda/releases).\nThen run:\n\n```sh\nlocalstack state import /path/to/release-pod.zip\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocalstack-samples%2Fsample-serverless-image-resizer-s3-lambda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flocalstack-samples%2Fsample-serverless-image-resizer-s3-lambda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocalstack-samples%2Fsample-serverless-image-resizer-s3-lambda/lists"}