The awscdk-rootmail construct enhancements
Photo by unsplash

I have been using the construct my self and also needed some updates. Furthermore, I build and enhance the tools I need myself and learn new stuff on the way.

So here the latest updates. You can read the initial post here 👈

Update 2024-02-17

  • Migrating to aws-sdk-js-v3 👉 done in the PR #17 and share the experience in the referring post 👈
  • Figuring out a method to run the test stack exclusively for faster feedback and not using Lambda log debugging 👉 done in the integ tests PR #14
  • Fixing the cdk-nag errors and warning and/or add appropriate suppressions ✅
  • Adding a release job in projen: still WIP ⌛ see the PR #25

Update 2024-02-18

Now we can also pass in a custom Lambda function to process the received EMails, for example

import { S3 } from '@aws-sdk/client-s3';
import { ParsedMail, simpleParser } from 'mailparser';
// populated by default from the construct
const emailBucket = process.env.EMAIL_BUCKET;
const emailBucketArn = process.env.EMAIL_BUCKET_ARN;
const s3 = new S3();

// SESEventRecordsToLambda
// from
export const handler = async (event: SESEventRecordsToLambda) => {
    for (const record of event.Records) {
        const id =;
        const key = `RootMail/${id}`;
        const response = await s3.getObject({ Bucket: emailBucket as string, Key: key });
        const msg: ParsedMail = await simpleParser(response.Body as unknown as Buffer);
        let title = msg.subject;
        console.log(`Title: ${title} from emailBucketArn: ${emailBucketArn}`);
        // use the content of the email body 
        const body = msg.html;
        // add your custom code here ... 👩🏽‍💻

give it the additionally needed permissions, wrap it into a NodejsFunction

const customSesReceiveFunction = new NodejsFunction(stackUnderTest, 'custom-ses-receive-function', {
  // omitted for brevity

// Note 🗒️ : any additional permissions you need to add to the function yourself!
customSesReceiveFunction.addToRolePolicy(new iam.PolicyStatement({
  actions: [
  resources: ['*'],

and then pass it in

export class MyStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps = {}) {
    super(scope, id, props);

    const domain = 'mycompany.test'
    const hostedZone = r53.HostedZone.fromLookup(this, 'rootmail-parent-hosted-zone', {
      domainName: domain,

    const rootmail = new Rootmail(this, 'rootmail-stack', {
      domain: domain;
      autowireDNSParentHostedZoneID: hostedZone.hostedZoneId,
      env: {
        region: 'eu-west-1',
      customSesReceiveFunction: customSesReceiveFunction, // 👈🏽 pass it in here

And of course I added another integ-test for and could finally use of the --parallel-regions feature .

Migrate to aws-sdk-v3
Older post

Migrate to aws-sdk-v3

Lessons learned from migrating to the new AWS SDK for JavaScript (v3).

Newer post

Release with GitHub actions to S3

How to release your cdk construct to an S3 bucket 🪣 in projen with GitHub Actions

Release with GitHub actions to S3