Création d'une fonction AWS Lambda multimode avec Python et Terraform
Exemple étape par étape
Voici un exemple de traitement de message SQS avec Python Lambda + API REST avec protection par clé API + Terraform script pour le déployer en exécution serverless.
AWS Lambda vous permet d’écrire des fonctions serverless légères qui peuvent réagir à presque tout type d’événement — des messages SQS aux requêtes HTTP. Dans ce guide, nous allons construire une unique fonction Python Lambda qui fonctionne en deux modes :
- Mode SQS : Lorsqu’elle est déclenchée par un message SQS comme
{ "par": 10 }
, elle publie{ "res": 11 }
dans une autre file d’attente. - Mode HTTP : Lorsqu’elle est appelée via API Gateway à
GET /lam?par=10
, elle renvoie{ "res": 11 }
au client.
Nous allons également protéger l’extrémité HTTP à l’aide d’une clé API simple codée en dur — "testkey"
.
Tout le déploiement sera effectué à l’aide de Terraform.
Aperçu de l’architecture
Visualisons ce que nous construisons :
La même Lambda réagit à :
- Événements SQS, via une carte de source d’événement, et
- Demandes API Gateway, via une intégration HTTP RESTful.
Étape 1 : Créer la Lambda en Python
Créons un gestionnaire très simple en Python qui peut distinguer un événement SQS d’une appel HTTP.
Fichier : lambda_function.py
import json
import os
import boto3
sqs = boto3.client("sqs")
OUTPUT_QUEUE_URL = os.environ.get("OUTPUT_QUEUE_URL")
API_KEY = os.environ.get("API_KEY", "testkey") # valeur par défaut codée en dur
def lambda_handler(event, context):
# Détecter le type d'événement
if "Records" in event: # événement SQS
return handle_sqs(event["Records"])
else: # événement HTTP
return handle_http(event)
def handle_sqs(records):
for record in records:
body = json.loads(record["body"])
par = int(body["par"])
res = par + 1
message = json.dumps({"res": res})
sqs.send_message(QueueUrl=OUTPUT_QUEUE_URL, MessageBody=message)
return {"status": "processed", "count": len(records)}
def handle_http(event):
headers = {k.lower(): v for k, v in (event.get("headers") or {}).items()}
if headers.get("x-api-key") != API_KEY:
return {
"statusCode": 403,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "Interdit"})
}
params = event.get("queryStringParameters") or {}
if "par" not in params:
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "Paramètre 'par' manquant"})
}
par = int(params["par"])
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"res": par + 1})
}
Ce que nous avons dans cette fonction Lambda :
- Les messages SQS sont analysés en JSON.
- Lorsqu’elle est déclenchée par API Gateway, la fonction vérifie la clé API et le paramètre de requête.
- L’URL de la file de sortie et la clé API sont transmises via des variables d’environnement.
Étape 2 : Déployer avec Terraform
Terraform nous permet de configurer déclarativement l’infrastructure AWS — Lambda, files SQS, API Gateway et autorisations — en une seule fois.
Structure du projet :
project/
├── lambda/
│ └── lambda_function.py
└── infra/
└── main.tf
Configuration Terraform (infra/main.tf
)
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
archive = {
source = "hashicorp/archive"
}
}
}
provider "aws" {
region = "us-east-1"
}
locals {
project = "lambda-sqs-api"
}
# Empaquetage de la Lambda
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "../lambda"
output_path = "lambda.zip"
}
# Files SQS
resource "aws_sqs_queue" "input" {
name = "${local.project}-input"
}
resource "aws_sqs_queue" "output" {
name = "${local.project}-output"
}
# Rôle IAM pour la Lambda
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "lambda_role" {
name = "${local.project}-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_policy" "lambda_policy" {
name = "${local.project}-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "lambda_policy_attach" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_policy.arn
}
# Fonction Lambda
resource "aws_lambda_function" "func" {
filename = data.archive_file.lambda_zip.output_path
function_name = local.project
role = aws_iam_role.lambda_role.arn
handler = "lambda_function.lambda_handler"
runtime = "python3.12"
environment {
variables = {
OUTPUT_QUEUE_URL = aws_sqs_queue.output.id
API_KEY = "testkey"
}
}
}
# Carte de source d'événement (SQS → Lambda)
resource "aws_lambda_event_source_mapping" "sqs_trigger" {
event_source_arn = aws_sqs_queue.input.arn
function_name = aws_lambda_function.func.arn
batch_size = 1
enabled = true
}
# API Gateway
resource "aws_api_gateway_rest_api" "api" {
name = "${local.project}-api"
}
resource "aws_api_gateway_resource" "lam" {
rest_api_id = aws_api_gateway_rest_api.api.id
parent_id = aws_api_gateway_rest_api.api.root_resource_id
path_part = "lam"
}
resource "aws_api_gateway_method" "get_lam" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.lam.id
http_method = "GET"
authorization = "NONE"
api_key_required = true
}
resource "aws_api_gateway_integration" "lambda_integration" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.lam.id
http_method = aws_api_gateway_method.get_lam.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.func.invoke_arn
}
resource "aws_lambda_permission" "api_gateway" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.func.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.api.execution_arn}/*/*"
}
# Clé API et plan d'utilisation
resource "aws_api_gateway_api_key" "key" {
name = "testkey"
value = "testkey"
enabled = true
}
resource "aws_api_gateway_usage_plan" "plan" {
name = "basic"
api_stages {
api_id = aws_api_gateway_rest_api.api.id
stage = aws_api_gateway_deployment.deploy.stage_name
}
}
resource "aws_api_gateway_usage_plan_key" "plan_key" {
key_id = aws_api_gateway_api_key.key.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.plan.id
}
resource "aws_api_gateway_deployment" "deploy" {
depends_on = [aws_api_gateway_integration.lambda_integration]
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = "v1"
}
output "api_url" {
value = "${aws_api_gateway_deployment.deploy.invoke_url}/lam"
}
Étape 3 : Déployer et tester
- Initialiser Terraform :
cd infra
terraform init
- Appliquer la configuration :
terraform apply
- Tester l’extrémité API Gateway :
curl -H "x-api-key: testkey" "<API_URL>?par=10"
# Résultat attendu : {"res": 11}
- Tester SQS :
Envoyer un message à la file d’entrée :
aws sqs send-message --queue-url <input-queue-url> --message-body '{"par": 5}'
Puis vérifier la file de sortie :
aws sqs receive-message --queue-url <output-queue-url>
# Résultat attendu : {"res": 6}
Étape 4 : Nettoyage
Pour supprimer toutes les ressources :
terraform destroy
Résumé
[File d'attente d'entrée SQS] ─▶ [Fonction Lambda] ─▶ [File d'attente de sortie SQS]
▲
│
[API Gateway /lam?par=N]
│
Sécurisé par une clé API
Vous avez tout juste construit une Lambda multi-triggers qui :
- Consomme et publie des messages dans des files SQS.
- Répond aux requêtes HTTP via API Gateway.
- Applique une clé API via une vérification simple d’en-tête.
- Est entièrement gérée via Terraform pour une infrastructure serverless reproductible.
Ce modèle est idéal pour des transformateurs de messages légers, des microservices hybrides, ou pour relier des systèmes AWS asynchrones et synchrones — tout avec quelques lignes de Python et de Terraform.
Si vous souhaitez voir un exemple un peu plus avancé de Lambda utilisant AWS SAM - veuillez consulter cet article : Codage Lambda avec AWS SAM + AWS SQS + Python PowerTools
Liens utiles
- Feuille de rappel Python
- Feuille de rappel Terraform - commandes utiles et exemples
- Performance d’AWS Lambda : JavaScript vs Python vs Golang
- Lambda en couches avec AWS SAM et Python
- Codage Lambda avec AWS SAM + AWS SQS + Python PowerTools
- Aperçu d’AWS CDK, exemples en TypeScript et Python et considérations de performance