-
Notifications
You must be signed in to change notification settings - Fork 1k
Ras al jil feature eventbridge scheduler ses abandoned cart notification #2998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,304 @@ | ||||||||||||||
| # Amazon EventBridge Scheduler to AWS Lambda to Amazon SES | ||||||||||||||
|
|
||||||||||||||
| This pattern demonstrates how to use Amazon EventBridge Scheduler to drive per-customer abandoned cart email notifications on an hourly cadence. A Lambda function, invoked by the scheduler, queries a DynamoDB GSI for customers with abandoned carts that have not yet been notified, sends each a personalised HTML email via Amazon SES, and marks the record as notified to prevent duplicate emails. The pattern includes idempotent notification logic, seed test data, a dead-letter queue for failed scheduler invocations, and least-privilege IAM policies scoped to the specific SES identity and DynamoDB table. | ||||||||||||||
|
|
||||||||||||||
| Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-scheduler-ses-abandoned-cart-notification | ||||||||||||||
|
|
||||||||||||||
| 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 | ||||||||||||||
|
|
||||||||||||||
| * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. | ||||||||||||||
| * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured | ||||||||||||||
| * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) | ||||||||||||||
| * [Terraform](https://developer.hashicorp.com/terraform/install) (>= 1.0) installed | ||||||||||||||
|
|
||||||||||||||
| ### Prerequisites — Amazon SES Identity | ||||||||||||||
|
|
||||||||||||||
| This pattern sends emails through Amazon SES. You **must** have a verified SES identity (email address or domain) before deploying. | ||||||||||||||
|
|
||||||||||||||
| 1. **Verify a sender email address** (or domain) in the [SES console](https://console.aws.amazon.com/ses/home#/verified-identities) or via the CLI: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws ses verify-email-identity --email-address noreply@yourdomain.com | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 2. **Check your inbox** and click the verification link sent by AWS. | ||||||||||||||
|
|
||||||||||||||
| 3. **If your SES account is in sandbox mode** (the default for new accounts), you must also verify every **recipient** email address and ensure `ses:SendEmail` permissions include the recipient identity. For the seed test data included in this pattern: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws ses verify-email-identity --email-address rajilpaloth@gmail.com | ||||||||||||||
| ``` | ||||||||||||||
|
Comment on lines
+30
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The README and seed test data reference a personal email address ( In total three occurences, please remove your personal email and note, that it needs to be changed to the one being used and where. (lines 37-39, 148, 165)
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| > **Note:** In sandbox mode, SES requires `ses:SendEmail` permission for both the sender and recipient identities. If your SES account has **production access** enabled, recipient verification and permissions are not required. | ||||||||||||||
|
|
||||||||||||||
| 4. **Note your SES identity ARN** — you will need it during deployment: | ||||||||||||||
|
|
||||||||||||||
| ``` | ||||||||||||||
| arn:aws:ses:{region}:{account-id}:identity/noreply@yourdomain.com | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| You can retrieve it with: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| echo "arn:aws:ses:$(aws configure get region):$(aws sts get-caller-identity --query Account --output text):identity/noreply@yourdomain.com" | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 5. Confirm both identities show `"VerificationStatus": "Success"`: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws ses get-identity-verification-attributes \ | ||||||||||||||
| --identities noreply@yourdomain.com rajilpaloth@gmail.com | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ### Prerequisites — DynamoDB Table Population | ||||||||||||||
|
|
||||||||||||||
| This pattern deploys the DynamoDB table and seeds it with three test records for demonstration purposes. In a production environment, you will need a **separate system or mechanism** to populate the DynamoDB table with real customer data whenever a cart is abandoned. Common approaches include: | ||||||||||||||
|
|
||||||||||||||
| - An **API Gateway + Lambda** endpoint called by your e-commerce application when a cart is abandoned | ||||||||||||||
| - A **DynamoDB Streams** consumer that reacts to cart updates and sets the `CartAbandoned` flag | ||||||||||||||
| - A **Step Functions** workflow that monitors cart activity and marks carts as abandoned after a timeout period | ||||||||||||||
| - A direct **SDK write** from your application backend | ||||||||||||||
|
|
||||||||||||||
| The notification processor Lambda in this pattern only **reads** the table and **updates** the `NotificationSent` flag — it does not create or manage cart records. | ||||||||||||||
|
|
||||||||||||||
| **DynamoDB record schema expected by the Lambda function:** | ||||||||||||||
|
|
||||||||||||||
| | Attribute | Type | Description | | ||||||||||||||
| |---|---|---| | ||||||||||||||
| | `CustomerId` | String (Hash Key) | Unique customer identifier | | ||||||||||||||
| | `Email` | String | Customer email address | | ||||||||||||||
| | `CustomerName` | String | Customer display name | | ||||||||||||||
| | `CartAbandoned` | String (`"true"` / `"false"`) | Whether the cart is abandoned (GSI hash key) | | ||||||||||||||
| | `NotificationSent` | String (`"true"` / `"false"`) | Whether the notification email has been sent | | ||||||||||||||
| | `CartItems` | List of Maps | Items in the cart (`ItemName`, `Price`) | | ||||||||||||||
| | `CartTotal` | Number | Total cart value | | ||||||||||||||
| | `CartAbandonedAt` | String (ISO 8601) | Timestamp when the cart was abandoned | | ||||||||||||||
|
|
||||||||||||||
| ## Architecture | ||||||||||||||
|
|
||||||||||||||
|  | ||||||||||||||
|
|
||||||||||||||
| EventBridge Scheduler (rate 1 hour) | ||||||||||||||
| │ | ||||||||||||||
| ├── on failure ──▶ SQS DLQ | ||||||||||||||
| │ | ||||||||||||||
| ▼ | ||||||||||||||
| Notification Processor Lambda | ||||||||||||||
| │ | ||||||||||||||
| ├── READ ──▶ DynamoDB (abandoned-carts) | ||||||||||||||
| │ query CartAbandoned = "true" | ||||||||||||||
| │ filter NotificationSent = "false" | ||||||||||||||
| │ | ||||||||||||||
| └── SEND ──▶ Amazon SES | ||||||||||||||
| per-customer abandoned cart email | ||||||||||||||
| then mark NotificationSent = "true" | ||||||||||||||
|
Comment on lines
+83
to
+96
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ascii chars do not render well. I would suggest combining the flow informations with the architecture diagram (e.g. numbered hints) and add a short description how the architecture works, while describing the flow and linking back to the architecture diagrams with the numbers. |
||||||||||||||
|
|
||||||||||||||
| ## Deployment Instructions | ||||||||||||||
|
|
||||||||||||||
| 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| git clone https://github.com/aws-samples/serverless-patterns | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 1. Change directory to the pattern directory: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| cd serverless-patterns/eventbridge-scheduler-ses-abandoned-cart-notification | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 1. Initialize Terraform: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| terraform init | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 1. Review the execution plan: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| terraform plan \ | ||||||||||||||
| -var="aws_region=us-east-1" \ | ||||||||||||||
| -var="prefix=cartnotify" \ | ||||||||||||||
| -var="ses_identity_arn=arn:aws:ses:us-east-1:123456789012:identity/noreply@yourdomain.com" \ | ||||||||||||||
| -var="sender_email=noreply@yourdomain.com" | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Replace the SES identity ARN, sender email, and region with your actual values. | ||||||||||||||
|
|
||||||||||||||
| 1. Deploy the resources: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| terraform apply \ | ||||||||||||||
| -var="aws_region=us-east-1" \ | ||||||||||||||
| -var="prefix=cartnotify" \ | ||||||||||||||
| -var="ses_identity_arn=arn:aws:ses:us-east-1:123456789012:identity/noreply@yourdomain.com" \ | ||||||||||||||
| -var="sender_email=noreply@yourdomain.com" | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Type `yes` when prompted. Deployment takes approximately 1–2 minutes. | ||||||||||||||
|
|
||||||||||||||
| 1. Note the outputs from the Terraform deployment process. These contain the resource names and ARNs used for testing: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| terraform output | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The README does not include a dedicated section for verifying that the deployment succeeded and resources were created correctly. While the Testing section covers functional testing, there is no guidance on confirming the Terraform deployment outputs, verifying the EventBridge schedule is active, or checking that the DynamoDB table was seeded correctly. As an example: ## Verify Deployment
After `terraform apply` completes, verify the resources were created:
1. Confirm the EventBridge schedule is active:
```bash
aws scheduler get-schedule --name $(terraform output -raw schedule_name)
```
2. Confirm the DynamoDB table has seed data:
```bash
aws dynamodb scan --table-name $(terraform output -raw dynamodb_table_name) --select COUNT
```
Expected: `Count: 3` |
||||||||||||||
| ## How it works | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be merged with the architecture -> textual version of the flow in combination with the architecture diagrams |
||||||||||||||
|
|
||||||||||||||
| 1. **EventBridge Scheduler** fires on the configured schedule (default: every hour) and invokes the **Notification Processor Lambda** function. | ||||||||||||||
|
|
||||||||||||||
| 2. The Lambda function queries the **DynamoDB Global Secondary Index** (`CartAbandonedIndex`) to retrieve all records where `CartAbandoned = "true"`. | ||||||||||||||
|
|
||||||||||||||
| 3. For each abandoned cart record, the function checks the `NotificationSent` attribute: | ||||||||||||||
| - If `"true"` → the customer has already been emailed, so the record is **skipped**. | ||||||||||||||
| - If `"false"` → the function proceeds to send an email. | ||||||||||||||
|
|
||||||||||||||
| 4. The function builds a **personalised HTML email** containing the customer's name, cart items, cart total, and a call-to-action button, then sends it via **Amazon SES**. | ||||||||||||||
|
|
||||||||||||||
| 5. After a successful send, the function **updates the DynamoDB record**, setting `NotificationSent = "true"` and recording a `NotifiedAt` timestamp. This ensures the customer is **never emailed twice** for the same abandoned cart, even if the scheduler fires again. | ||||||||||||||
|
|
||||||||||||||
| 6. If the scheduler fails to invoke the Lambda after 3 retries, the event is sent to the **SQS Dead-Letter Queue** for investigation. | ||||||||||||||
|
|
||||||||||||||
| **Seed test data included:** | ||||||||||||||
|
|
||||||||||||||
| | CustomerId | Email | CartAbandoned | NotificationSent | Expected Behaviour | | ||||||||||||||
| |---|---|---|---|---| | ||||||||||||||
| | `cust-001` | `rajilpaloth@gmail.com` | `true` | `false` | ✅ Will receive email | | ||||||||||||||
| | `cust-002` | `activecustomer@example.com` | `false` | `false` | ⏭️ Not abandoned — not queried | | ||||||||||||||
| | `cust-003` | `alreadynotified@example.com` | `true` | `true` | ⏭️ Already notified — skipped | | ||||||||||||||
|
|
||||||||||||||
| ## Testing | ||||||||||||||
|
|
||||||||||||||
| 1. **Invoke the Lambda function manually** (without waiting for the schedule): | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws lambda invoke \ | ||||||||||||||
| --function-name cartnotify-notification-processor \ | ||||||||||||||
| --cli-binary-format raw-in-base64-out \ | ||||||||||||||
| --payload '{ | ||||||||||||||
| "source": "manual-test", | ||||||||||||||
| "taskType": "abandoned-cart-notification" | ||||||||||||||
| }' \ | ||||||||||||||
| /dev/stdout 2>/dev/null | jq . | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Expected response: | ||||||||||||||
|
|
||||||||||||||
| ```json | ||||||||||||||
| { | ||||||||||||||
| "statusCode": 200, | ||||||||||||||
| "body": "{\"invokedAt\": \"2025-01-15T12:00:05Z\", \"notificationsSent\": 1, \"skipped\": 1, \"errors\": 0}" | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 2. **Check the recipient inbox** (`rajilpaloth@gmail.com`) for the abandoned cart email. Check the spam/junk folder if it does not appear in the inbox. | ||||||||||||||
|
|
||||||||||||||
| 3. **Verify the DynamoDB record was updated:** | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws dynamodb get-item \ | ||||||||||||||
| --table-name cartnotify-abandoned-carts \ | ||||||||||||||
| --key '{"CustomerId": {"S": "cust-001"}}' \ | ||||||||||||||
| --query "Item.{NotificationSent:NotificationSent.S,NotifiedAt:NotifiedAt.S}" \ | ||||||||||||||
| --output table | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Expected: | ||||||||||||||
|
|
||||||||||||||
| ``` | ||||||||||||||
| ────────────────────────────────────────── | ||||||||||||||
| | GetItem | | ||||||||||||||
| +------------------+---------------------+ | ||||||||||||||
| | NotificationSent | NotifiedAt | | ||||||||||||||
| +------------------+---------------------+ | ||||||||||||||
| | true | 2025-01-15T12:00:05Z| | ||||||||||||||
| +------------------+---------------------+ | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 4. **Verify idempotency** by invoking the Lambda again: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws lambda invoke \ | ||||||||||||||
| --function-name cartnotify-notification-processor \ | ||||||||||||||
| --cli-binary-format raw-in-base64-out \ | ||||||||||||||
| --payload '{"source": "idempotency-test"}' \ | ||||||||||||||
| /dev/stdout 2>/dev/null | jq . | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Expected — no duplicate emails sent: | ||||||||||||||
|
|
||||||||||||||
| ```json | ||||||||||||||
| { | ||||||||||||||
| "statusCode": 200, | ||||||||||||||
| "body": "{\"invokedAt\": \"...\", \"notificationsSent\": 0, \"skipped\": 2, \"errors\": 0}" | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 5. **Reset the test record** to re-test: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws dynamodb update-item \ | ||||||||||||||
| --table-name cartnotify-abandoned-carts \ | ||||||||||||||
| --key '{"CustomerId": {"S": "cust-001"}}' \ | ||||||||||||||
| --update-expression "SET NotificationSent = :f REMOVE NotifiedAt" \ | ||||||||||||||
| --expression-attribute-values '{":f": {"S": "false"}}' | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 6. **Check Lambda logs** for detailed execution information: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws logs tail /aws/lambda/cartnotify-notification-processor --follow | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 7. **Check the DLQ** for any failed scheduler invocations: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws sqs get-queue-attributes \ | ||||||||||||||
| --queue-url $(terraform output -raw dlq_queue_url) \ | ||||||||||||||
| --attribute-names ApproximateNumberOfMessages | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ## Troubleshooting | ||||||||||||||
|
|
||||||||||||||
| | Symptom | Cause | Fix | | ||||||||||||||
| |---|---|---| | ||||||||||||||
| | `MessageRejected: Email address is not verified` | SES sandbox — recipient not verified | Run `aws ses verify-email-identity --email-address rajilpaloth@gmail.com` and click the verification link | | ||||||||||||||
| | `AccessDenied` on `ses:SendEmail` | SES identity ARN mismatch | Verify the `ses_identity_arn` variable matches your sender email exactly | | ||||||||||||||
| | Lambda returns `notificationsSent: 0` | All abandoned carts already notified | Reset with the `update-item` command in the Testing section | | ||||||||||||||
| | No items returned from GSI query | GSI not yet backfilled | Wait 30 seconds after deploy for the GSI to populate | | ||||||||||||||
| | Schedule never fires | Schedule expression typo | Run `aws scheduler get-schedule --name cartnotify-abandoned-cart-notify` | | ||||||||||||||
| | DLQ filling up | Lambda timeout or SES errors | Check CloudWatch logs and increase the Lambda timeout if needed | | ||||||||||||||
|
|
||||||||||||||
| ## Cleanup | ||||||||||||||
|
|
||||||||||||||
| 1. Delete the stack: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| terraform destroy \ | ||||||||||||||
| -var="aws_region=us-east-1" \ | ||||||||||||||
| -var="prefix=cartnotify" \ | ||||||||||||||
| -var="ses_identity_arn=arn:aws:ses:us-east-1:123456789012:identity/noreply@yourdomain.com" \ | ||||||||||||||
| -var="sender_email=noreply@yourdomain.com" \ | ||||||||||||||
| -auto-approve | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 2. Confirm all resources have been removed: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws dynamodb describe-table --table-name cartnotify-abandoned-carts 2>&1 | grep -q "ResourceNotFoundException" && echo "Table deleted" || echo "Table still exists" | ||||||||||||||
| aws lambda get-function --function-name cartnotify-notification-processor 2>&1 | grep -q "ResourceNotFoundException" && echo "Lambda deleted" || echo "Lambda still exists" | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| 3. (Optional) Remove the SES verified identities if no longer needed: | ||||||||||||||
|
|
||||||||||||||
| ```bash | ||||||||||||||
| aws ses delete-verified-email-identity --email-address noreply@yourdomain.com | ||||||||||||||
| aws ses delete-verified-email-identity --email-address rajilpaloth@gmail.com | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ---- | ||||||||||||||
| Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||||||||||||||
|
|
||||||||||||||
| SPDX-License-Identifier: MIT-0 | ||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||
| { | ||||||
| "title": "EventBridge Scheduler + SES Integration- Per-customer notification scheduling (abandoned cart, billing reminders)", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "description": "Create a EventBridge scheduler which sends per customer notifcations for abandoned carts and billing reminders using SES.", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "language": "Python", | ||||||
| "level": "300", | ||||||
| "framework": "Terraform", | ||||||
| "introBox": { | ||||||
| "headline": "How it works", | ||||||
| "text": [ | ||||||
| "This pattern demonstrates how to use Amazon EventBridge Scheduler to drive per-customer abandoned cart email notifications on an hourly cadence. A Lambda function, invoked by the scheduler, queries a DynamoDB GSI for customers with abandoned carts that have not yet been notified, sends each a personalised HTML email via Amazon SES, and marks the record as notified to prevent duplicate emails. The pattern includes idempotent notification logic, seed test data, a dead-letter queue for failed scheduler invocations, and least-privilege IAM policies scoped to the specific SES identity and DynamoDB table." | ||||||
| ] | ||||||
| }, | ||||||
| "gitHub": { | ||||||
| "template": { | ||||||
| "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-scheduler-ses-abandoned-cart-notification", | ||||||
| "templateURL": "serverless-patterns/eventbridge-scheduler-ses-abandoned-cart-notification", | ||||||
| "projectFolder": "eventbridge-scheduler-ses-abandoned-cart-notification", | ||||||
| "templateFile": "main.tf" | ||||||
| } | ||||||
| }, | ||||||
| "resources": { | ||||||
| "bullets": [ | ||||||
| { | ||||||
| "text": "Verify Sender Email Address", | ||||||
| "link": "https://console.aws.amazon.com/ses/home#/verified-identities" | ||||||
| }, | ||||||
| { | ||||||
| "text": "Request production access (Moving out of the Amazon SES sandbox)", | ||||||
| "link": "https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html" | ||||||
| } | ||||||
| ] | ||||||
| }, | ||||||
| "deploy": { | ||||||
| "text": [ | ||||||
| "terraform init", | ||||||
| "terraform apply" | ||||||
| ] | ||||||
| }, | ||||||
| "testing": { | ||||||
| "text": [ | ||||||
| "See the GitHub repo for detailed testing instructions." | ||||||
| ] | ||||||
| }, | ||||||
| "cleanup": { | ||||||
| "text": [ | ||||||
| "terraform destroy", | ||||||
| "terraform show" | ||||||
| ] | ||||||
| }, | ||||||
| "authors": [ | ||||||
| { | ||||||
| "name": "Rajil Paloth", | ||||||
| "image": "https://i.ibb.co/r2TsqGf6/Passport-size.jpg", | ||||||
| "bio": "ProServe Delivery Consultant at AWS", | ||||||
| "linkedin": "paloth" | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can yo please use the latest architecture icons from https://aws.amazon.com/architecture/icons/
A little bit more spacing between the icons will improve readability