
CI/CD Fonction serverless d'envoi d'e-mails avec AWS SES et Lambda
par Rebeca Murillo • 20 décembre 2023
Introduction
Dans ce tutoriel, je vais vous guider à travers le processus de développement d’un service simple pour envoyer des e-mails avec Simple Email Service (AWS SES) et le déployer en tant que fonction serverless dans AWS Lambda directement depuis mon dépôt Github !
J’utilise ce service pour mon site web, pour le formulaire de contact. Je cherchais une solution simple et rapide pour envoyer des e-mails de manière sécurisée depuis mon site web, sans exposer publiquement les secrets de mon fournisseur de messagerie. La fonction serverless est la solution la plus rapide et la plus rentable pour déployer une route API REST sécurisée pour un service simple comme celui-ci.
Guide étape par étape
Le guide suivant se décompose en plusieurs étapes :
- Fonction JavaScript pour envoyer un e-mail avec
nodemailer - Déclaration de la fonction serverless avec fournisseur AWS
- Autorisation du dépôt Github via un rôle AWS
- Configuration du flux de travail du dépôt Github
Les prérequis pour ce guide :
- Connaissance de base de Node.js et de JavaScript
- Compte AWS
- Dépôt Github
1. Envoyer un e-mail avec nodemailer et aws-sdk
Le code suivant utilise nodemailer et aws-sdk pour envoyer des e-mails via le service Amazon Email Service SES.
La fonction handler est le point d’entrée de la fonction serverless, qui sera configurée ultérieurement.
La fonction utilise des variables d’environnement pour :
- la région AWS pour le service SES
AWS_REGION_SES - l’e-mail par défaut de l’expéditeur
DEFAULT_FROM_EMAIL - l’e-mail par défaut du destinataire
DEFAULT_TO_EMAIL
Fichier index.js
//index.js
const AWS = require("aws-sdk");
const nodemailer = require("nodemailer");
const AWS_REGION_SES = process.env.AWS_REGION_SES;
const DEFAULT_FROM_EMAIL = process.env.DEFAULT_FROM_EMAIL;
const DEFAULT_TO_EMAIL = process.env.DEFAULT_TO_EMAIL;
AWS.config.update({ region: AWS_REGION_SES });
const ses = new AWS.SES({ apiVersion: "2010-12-01" });
const transporter = nodemailer.createTransport({
SES: { ses, aws: { region: AWS_REGION_SES } },
});
exports.handler = async (event) => {
console.log("event: ", event);
const { recaptchaResponse, to, from, subject, text } = JSON.parse(event.body);
// Step 1: Validate input parameters
const inputValidationResult = validateInputParameters(
to,
from,
subject,
text
);
if (!inputValidationResult.valid) {
return response(400, { message: "Invalid input parameters" });
}
// Step 2: Send email
const emailSendingResult = await sendEmail(to, from, subject, text);
if (emailSendingResult.error) {
return response(500, {
message: "Error sending message",
error: emailSendingResult.error,
});
}
return response(200, { message: "Email sent successfully" });
};
function response(statusCode, body) {
return {
statusCode,
body: JSON.stringify(body),
};
}
function validateInputParameters(to, from, subject, text) {
if (
typeof to !== "string" ||
typeof from !== "string" ||
typeof subject !== "string" ||
typeof text !== "string"
) {
return { valid: false };
}
return { valid: true };
}
async function sendEmail(to, from, subject, text) {
const params = {
from: from || DEFAULT_FROM_EMAIL,
to: to || DEFAULT_TO_EMAIL,
subject,
text,
};
try {
let info = await transporter.sendMail(params);
console.log("Message sent: %s", info.messageId);
return { success: true };
} catch (error) {
console.error("Error sending message: ", error);
return { error };
}
}
2. Fonction serverless pour le fournisseur AWS
Le code suivant déclare une fonction serverless qui sera interprétée par le package serverless. Le fichier serverless.yml doit être placé à la racine du référentiel.
Le déploiement de la fonction est ensuite lancé depuis la ligne de commande :
npx serverless deploy
Consultez la documentation AWS pour Créer des fonctions Lambda avec Node.js
Dans le code suivant, nous pouvons mettre en évidence :
- définition du fournisseur AWS
- utilisation de la version Nodejs 20
- déploiement dans une région AWS spécifiée en tant que paramètre
- définition des permissions nécessaires pour la fonction, dans ce cas, ce sont des actions SES
- définition des variables d’environnement à partir des paramètres d’instruction
- déclaration d’une fonction nommée
sendEmail, son point d’entrée est la fonctionhandlerdéclarée dans le fichier indexindex.handler - création une route API REST en tant que POST
/message
Fichier serverless.yml
# serverless.yml
service: nodemailer-ses
provider:
name: aws
runtime: nodejs20.x
region: ${param:AWS_REGION}
stage: production
iam:
role:
statements:
- Effect: "Allow"
Action:
- "ses:SendEmail"
- "ses:SendRawEmail"
Resource: "*"
environment:
AWS_REGION_SES: ${param:AWS_REGION}
DEFAULT_TO_EMAIL: ${param:DEFAULT_TO_EMAIL}
DEFAULT_FROM_EMAIL: ${param:DEFAULT_FROM_EMAIL}
functions:
sendEmail:
handler: index.handler
name: sendEmail
description: Send email using nodemailer and AWS SES
events:
- http:
path: message
method: post
Les variables d’environnement sont transmises avec l’option param :
npx serverless deploy --param="AWS_REGION=${{ env.AWS_REGION }}" --param="DEFAULT_FROM_EMAIL=${{ env.DEFAULT_FROM_EMAIL }}" --param="DEFAULT_TO_EMAIL=${{ env.DEFAULT_TO_EMAIL }}"
Cette configuration lance une stack AWS Cloudfront avec un modèle nodejs, qui effectue les actions suivantes :
- copie les sources du projet dans un compartiment S3
- crée une fonction Lambda
- configure API Gateway pour créer une route REST qui pointe vers la fonction
- crée un groupe de journaux de logs dans CloudWatch
- crée un rôle IAM avec les autorisations correspondantes pour la fonction
On peut lancer les étapes 1 et 2 localement sur notre poste. On peut aller aux étapes 3 et 4 pour automatiser le lancement et mise à jour de notre fonction serverles depuis notre dépôt Github via Github Actions
3. Autorisez votre référentiel Github avec un rôle AWS
Ensuite, nous allons configurer notre compte AWS pour le connecter à GitHub Actions. Pour cela, nous devons créer un rôle dans AWS. Ce rôle sera utilisé pour s’authentifier avec AWS, dans le flux de travail GitHub Actions.
Consultez la documentation AWS pour plus de détails sur ce processus : Utiliser des rôles IAM pour connecter GitHub Actions à des actions dans AWS
3.1. Créer un fournisseur OIDC pour Github
Pour commencer, nous devons créer un fournisseur d’identité pour GitHub.
- Dans la console AWS, accédez à Identity and Access Management (IAM), sous Access Management > Identity Providers
- Cliquez sur ‘Ajouter un nouveau fournisseur’ :
- Type de fournisseur : ‘Fournisseur Open ID Connect’
- URL du fournisseur :
token.actions.githubusercontent.com - Audience :
sts.amazonaws.com.
Une fois notre fournisseur d’identité établi, nous pouvons passer à la création du rôle.
3.2. Créer le rôle AWS
Dans la console AWS, accédez à Identity and Access Management (IAM), sous Access Management > ‘Rôles’
- Cliquez pour créer un “Nouveau rôle” :
- Type d’entité : Identité Web.
- Sélectionnez le fournisseur d’identité que nous avons créé à l’étape précédente. L’audience sera celle par défaut.
- L’organisation GitHub est essentiellement votre compte GitHub où le projet est hébergé.
- Le nom de votre référentiel GitHub
- La branche de votre référentiel Github qui sera autorisée à exécuter le flux de travail Github Actions
- À l’étape des stratégies d’autorisations, configurez les règles en fonction des actions qui seront exécutées par le flux de travail Github Actions. Pour ce tutoriel, les autorisations requises sont :
- Accès complet à CloudFormation : qui exécute et orchestre le processus de déploiement
- API Gateway : pour publier la route de l’API REST pour la fonction
- Accès S3 : utilisé pour copier le contenu du référentiel
- AWS Lambda : pour le déploiement de la fonction serverless
- Accès IAM : utilisé pour créer un rôle avec les autorisations spécifiques à la fonction
- Enregistrez le nouveau rôle

Après avoir créé le rôle, ouvrez-le pour copier l’ARN (Amazon Resource Name), qui sera nécessaire à l’étape suivante pour configurer les secrets dans GitHub.
4. Flux de travail Github Actions pour le déploiement serverless sur AWS
Enfin, le référentiel est configuré pour exécuter le déploiement avec un flux de travail Github Actions.
Le fichier suivant déclare un flux de travail Github Actions qui exécute les actions suivantes :
- s’exécute manuellement, pour personnaliser le déclencheur du flux de travail, consultez la documentation de Github sur les événements qui déclenchent les flux de travail
- définit les autorisations pour la connexion avec AWS
- définit les variables d’environnement à partir des secrets Github
- se connecte à AWS via un rôle AWS avec l’action
aws-actions/configure-aws-credentials - déploie la fonction avec le package node serverless et passe les paramètres
npx serverless deploy --param="AWS_REGION=${{ env.AWS_REGION }}"…
name: Deploy to AWS Lambda
on:
workflow_dispatch:
permissions:
id-token: write
contents: read
env:
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
DEFAULT_FROM_EMAIL: ${{ secrets.DEFAULT_FROM_EMAIL }}
DEFAULT_TO_EMAIL: ${{ secrets.DEFAULT_TO_EMAIL }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy Lambda function
run: |
run: |
npx serverless deploy --param="AWS_REGION=${{ env.AWS_REGION }}" \
--param="DEFAULT_FROM_EMAIL=${{ env.DEFAULT_FROM_EMAIL }}" \
--param="DEFAULT_TO_EMAIL=${{ env.DEFAULT_TO_EMAIL }}"
Configurez les secrets sur votre dépôt Github, sous Paramètres > Secrets et variables > Actions
- AWS_ROLE_ARN : le rôle créé à l’étape 3.2
- AWS_REGION : la région AWS où les ressources seront déployées
- DEFAULT_FROM_EMAIL : l’adresse e-mail par défaut pour la fonction
- DEFAULT_TO_EMAIL : l’adresse e-mail par défaut pour la fonction
Pour exécuter le flux de travail sur votre dépôt Github, sous Actions > sélectionnez le flux de travail > exécuter le flux de travail

Si la configuration est correcte, vous verrez l’avancement de votre déploiement sur votre compte AWS, dans CloudFormation.

Identifiez l’endpoint de votre service dans l’onglet “Outputs” de votre stack CloudFormation. Vous pouvez maintenant appeler votre fonction avec le chemin complet.
Dans notre exemple, la fonction est disponible en appelant la route suivante :
curl -X POST https://<unique id for resource>.execute-api.eu-west-3.amazonaws.com/production/message \
-H "Content-Type: application/json" \
-d '{
"to": "myemail@gmail.com",
"from": "myemail@gmail.com",
"subject": "my new email",
"text": "my email content text"
}'
Maintenant, vous pouvez commencer à appeler cet endpoint pour envoyer des e-mails avec le service AWS SES.
Pour aller plus loin, nous pouvons réfléchir à la manière de sécuriser notre fonction serverless. Cela fera l’objet d’un tout nouveau tutoriel à venir bientôt…
