---
title: "Alertes CloudWatch par appel sans Lambda ni Twilio"
description: "SNS ne peut pas appeler un téléphone. Routez les alarmes CloudWatch via EventBridge vers un webhook d'appel : mise en place, filtres et pièges."
date: 2026-10-16
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - AWS
  - CloudWatch
  - EventBridge
  - alertes par appel
  - webhooks
  - astreinte
---

# Alertes CloudWatch par appel : d'EventBridge à votre téléphone, sans Lambda

Une alarme CloudWatch ne peut pas appeler votre téléphone. Ses seules actions intégrées sont SNS, Auto Scaling, EC2 et Systems Manager, et SNS finit en e-mail, en SMS ou dans une file que vous devez surveiller. Les tutoriels qui font réellement sonner un téléphone montent une fonction Lambda et un compte Twilio. Vous pouvez vous passer des deux : laissez EventBridge recevoir le changement d'état de l'alarme, le remodeler en un petit corps JSON et l'envoyer en POST à un canal d'appel Echobell.

Ce guide couvre tout le chemin : le canal, la règle EventBridge, le transformateur d'entrée, les filtres qui gardent la sonnerie pertinente, et pourquoi brancher SNS directement sur le webhook semble devoir fonctionner alors que ce n'est presque jamais le cas.

## Pourquoi la route SNS habituelle devient pénible

Le conseil par défaut est « alarme → topic SNS → abonné ». Pour un appel téléphonique, cela pose trois problèmes :

- **SNS n'a pas de protocole vocal.** Les types d'abonnés sont e-mail, SMS, push mobile, SQS, Lambda, Firehose et HTTP(S). Le SMS se manque facilement et est bloqué par Ne pas déranger ; un appel vocal demande quelque chose que vous construisez vous-même.
- **Le message HTTPS est une chaîne dans une chaîne.** SNS livre son enveloppe avec `Content-Type: text/plain`, et l'alarme est un document JSON sérialisé dans le champ `Message` de l'enveloppe. Un récepteur de webhook qui lit des champs — `AlarmName`, `NewStateValue` — ne trouve qu'un bloc opaque.
- **L'enveloppe est volumineuse.** Entre la signature, l'URL du certificat de signature et le lien de désabonnement, l'enveloppe ajoute bien plus d'un kilo-octet avant même que le texte de votre alarme ne commence. Echobell tronque le corps d'une notification à 1500 octets : un modèle « tout déverser » est donc coupé juste là où commence la partie utile.

On peut faire fonctionner SNS avec une Lambda qui déballe le message et le retransmet. Mais CloudWatch publie déjà chaque changement d'état d'alarme vers EventBridge sous forme d'événement JSON propre — sans aucune action à configurer — et EventBridge peut le remodeler et appeler lui-même un endpoint HTTPS.

## Ce qu'il vous faut

- Un compte AWS et l'autorisation de créer des règles, connexions et API destinations EventBridge
- Echobell installé ([App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-fr&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid))
- Au moins une alarme CloudWatch existante
- Dix minutes

Rien n'a besoin d'accepter du trafic entrant de votre côté. C'est EventBridge qui émet la requête sortante.

## Étape 1 — Créer un canal qui sonne

Dans Echobell, créez un canal et réglez son type de notification sur **Appel**. Un canal d'appel se comporte comme un appel entrant plutôt que comme un push, il traverse donc le mode Concentration d'iOS et Ne pas déranger.

Le transformateur de l'étape 3 envoie un corps plat avec les champs `alarm`, `state`, `reason`, `region` et `link`, ce qui garde les modèles courts :

```
Titre : {{state}}: {{alarm}}
Corps : {{reason}}
```

Configurez le **modèle de lien** dans les réglages avancés pour qu'un appui sur la notification ouvre l'alarme dans la console :

```
{{link}}
```

Copiez l'URL du webhook du canal depuis la vue détaillée :

```
https://hook.echobell.one/t/<channel-token>
```

## Étape 2 — Créer la connexion et l'API destination

EventBridge appelle les endpoints HTTPS externes via une **API destination**, qui a besoin d'une **connexion** portant l'authentification. Echobell s'authentifie par le jeton secret présent dans l'URL : la connexion est donc une formalité — mais EventBridge ne crée pas de destination sans elle.

1. Ouvrez **EventBridge → Integration → API destinations → Connections → Create connection**
2. **Name** : `echobell`
3. **Authorization type** : **API key**
4. **API key name** : `x-source`, **Value** : `eventbridge`
5. Créez-la, puis allez dans **API destinations → Create API destination**
6. **Name** : `echobell-calling`
7. **API destination endpoint** : l'URL de votre canal Echobell de l'étape 1
8. **HTTP method** : `POST`
9. **Invocation rate limit** : `1` par seconde suffit largement et sert de soupape de sécurité
10. **Connection** : `echobell`

L'en-tête de la clé d'API est envoyé à chaque requête. Vous pouvez le vérifier dans une condition de canal Echobell si vous voulez une seconde barrière devant le jeton :

```
header["x-source"] == "eventbridge"
```

## Étape 3 — Créer la règle et le transformateur d'entrée

Allez dans **EventBridge → Rules → Create rule**, dans la même région que vos alarmes. Les règles sont régionales : une alarme en `eu-west-1` est invisible pour une règle en `us-east-1`.

Utilisez le bus d'événements **default** et une règle de type **event pattern**. Ce motif correspond à chaque alarme qui passe en `ALARM` :

```json
{
  "source": ["aws.cloudwatch"],
  "detail-type": ["CloudWatch Alarm State Change"],
  "detail": {
    "state": { "value": ["ALARM"] }
  }
}
```

Pour la cible, choisissez **EventBridge API destination** et sélectionnez `echobell-calling`. Laissez la console créer un nouveau rôle d'exécution — il accorde `events:InvokeApiDestination` pour cette destination et rien d'autre.

Puis déployez **Additional settings → Configure target input → Input transformer** :

**Input path**

```json
{
  "alarm": "$.detail.alarmName",
  "state": "$.detail.state.value",
  "reason": "$.detail.state.reason",
  "region": "$.region",
  "time": "$.time"
}
```

**Template**

```json
{
  "alarm": "<alarm>",
  "state": "<state>",
  "reason": "<reason>",
  "region": "<region>",
  "time": "<time>",
  "link": "https://console.aws.amazon.com/cloudwatch/home?region=<region>#alarmsV2:alarm/<alarm>"
}
```

EventBridge envoie le résultat en `application/json`, ce qui permet à Echobell de lire directement `{{alarm}}` et `{{state}}`.

## Étape 4 — Savoir à quoi ressemble vraiment l'événement

L'input path lit l'événement que CloudWatch publie. Abrégé, il ressemble à ceci :

```json
{
  "version": "0",
  "id": "c4c1c1c9-6542-e61b-6ef0-8c4d36933a92",
  "detail-type": "CloudWatch Alarm State Change",
  "source": "aws.cloudwatch",
  "account": "123456789012",
  "time": "2026-10-16T03:12:44Z",
  "region": "us-east-1",
  "resources": ["arn:aws:cloudwatch:us-east-1:123456789012:alarm:prod-api-5xx"],
  "detail": {
    "alarmName": "prod-api-5xx",
    "state": {
      "value": "ALARM",
      "reason": "Threshold Crossed: 1 out of the last 1 datapoints [42.0] was greater than the threshold (10.0).",
      "timestamp": "2026-10-16T03:12:44.000+0000"
    },
    "previousState": { "value": "OK" },
    "configuration": { "description": "API 5xx errors on the prod load balancer" }
  }
}
```

Ce qu'il faut savoir avant de modifier un modèle :

- **L'alarme se trouve sous `detail`.** `$.alarmName` ne correspond à rien ; `$.detail.alarmName` si.
- **`state.reason` est un texte généré.** Il nomme le point de données et le seuil, ce qui est exactement ce que vous voulez entendre à 3 h du matin.
- **Les noms d'alarme sont insérés tels quels.** Un guillemet double dans un nom d'alarme ou dans la raison casserait le JSON produit par le transformateur. Limitez-vous aux lettres, chiffres, tirets et tirets bas dans les noms d'alarme — ce qui est de toute façon une bonne pratique — sinon l'appel échouera avec une erreur 400.
- **Le lien demande un nom simple.** Un nom contenant des espaces ou `#` ne produira pas un lien profond correct ; la recherche de la console le retrouve quand même.

## Étape 5 — Filtrer jusqu'à ce qui mérite un appel

Un canal d'alarmes qui sonne pour chaque `ALARM` est le moyen le plus rapide de vous apprendre à l'ignorer. Filtrez à deux endroits.

**Dans le motif EventBridge**, évalué avant que quoi que ce soit ne soit envoyé :

| Objectif | Modification du motif |
| --- | --- |
| Uniquement les alarmes de production | `"alarmName": [{ "prefix": "prod-" }]` dans `detail` |
| Ignorer ce qui n'est pas urgent | Nommez-les `warn-…` et utilisez `"alarmName": [{ "anything-but": { "prefix": "warn-" } }]` |
| Uniquement des alarmes précises | `"alarmName": ["prod-api-5xx", "prod-db-cpu"]` |
| Uniquement les vraies alarmes, pas les états de test | Gardez `"state": { "value": ["ALARM"] }` — un événement `INSUFFICIENT_DATA` ne correspond jamais |
| Un seul compte ou une seule région | Ajoutez `"account": ["123456789012"]` ou `"region": ["us-east-1"]` au niveau supérieur |

**Dans Echobell**, utilisez une condition de canal pour ce que le motif ne sait pas exprimer, comme une plage horaire :

```
state == "ALARM" && (hour >= 17 || hour < 9)
```

Les variables de temps d'Echobell sont en UTC : convertissez donc d'abord depuis votre fuseau. La [référence des conditions](/fr/docs/conditions) donne la liste de toutes les variables.

## Étape 6 — Une porte plus discrète pour les retours à la normale

Une seconde règle avec `"value": ["OK"]` peut vous prévenir que le problème est résolu, sans sonner. Créez un second canal Echobell de type **Sensible au temps** (ou normal), une seconde API destination qui pointe vers lui, et réutilisez le même transformateur d'entrée. Le modèle de titre `{{state}}: {{alarm}}` se lit déjà « OK: prod-api-5xx ».

| Règle | État | Type de canal | Comportement |
| --- | --- | --- | --- |
| `alarms-to-call` | `ALARM`, préfixe `prod-` | Appel | Sonne à travers Concentration |
| `alarms-warn` | `ALARM`, préfixe `warn-` | Sensible au temps | Écran verrouillé, sans sonner |
| `alarms-recovered` | `OK` | Normal | Push ordinaire |

## Testez avant de faire confiance

N'attendez pas que la production casse. Forcez une alarme dans l'état voulu :

```bash
aws cloudwatch set-alarm-state \
  --alarm-name prod-api-5xx \
  --state-value ALARM \
  --state-reason "Manual test of the Echobell path"
```

L'alarme revient à son état réel à sa prochaine évaluation, mais l'événement de changement d'état est réel et traverse votre règle. Pour ne tester que le côté Echobell, déclenchez directement le canal :

```bash
curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"alarm":"prod-api-5xx","state":"ALARM","reason":"Manual test","region":"us-east-1","link":"https://console.aws.amazon.com/cloudwatch/"}'
```

Si le `curl` sonne et que le test d'alarme non, le problème est dans la règle, le transformateur ou la destination — pas dans le canal.

## Et SNS avec l'e-mail vers appel ?

Si vous avez déjà un topic SNS partout et ne voulez pas toucher à EventBridge, deux autres routes existent :

- **SNS → Lambda → webhook.** Quelques lignes de code analysent `Message`, puis envoient en POST un corps plat à la même URL Echobell. Ça marche, mais vous êtes désormais responsable d'une fonction, d'un rôle et de ses logs.
- **SNS → e-mail → déclencheur e-mail Echobell.** Abonnez l'[adresse du déclencheur e-mail](/fr/docs/email-trigger) du canal au topic. L'alarme arrive sous forme d'e-mail, et `{{subject}}` et `{{text}}` dans vos modèles deviennent la notification. C'est la moins configurée de toutes, avec la réserve que les abonnements e-mail SNS demandent un clic de confirmation unique et que le texte n'est pas remodelé.

Abonner directement l'URL du webhook Echobell au topic comme endpoint HTTPS est la route à éviter : la requête de confirmation arrive comme une notification, et chaque alarme suivante est la longue enveloppe décrite plus haut.

## Limites qui comptent pendant un incident

- **1 Mio de corps.** Echobell rejette avec un HTTP 413 les corps de déclencheur de plus de 1 Mio. Une alarme transformée fait quelques centaines d'octets : vous ne l'atteindrez pas ici.
- **120 requêtes par minute et par jeton.** Au-delà, le déclencheur répond `429` avec `RATE_LIMIT_EXCEEDED` et un `Retry-After`. La limite de 1 par seconde de la destination vous maintient en dessous, et EventBridge réessaie les événements qu'il n'a pas pu livrer.
- **1500 octets de corps de notification.** Les rendus plus longs sont tronqués. `state.reason` fait une phrase ou deux et tient.

## Ce que ce montage ne vous donne pas

- **Pas d'acquittement.** Répondre à l'appel ne dit rien à CloudWatch et ne fait pas taire les autres abonnés.
- **Pas d'escalade ni de rotation.** Tous les abonnés du canal sont appelés ; personne n'est appelé ensuite.
- **Pas de déduplication au-delà de l'alarme.** Une alarme instable qui passe de `OK → ALARM` toutes les quelques minutes sonnera à chaque fois. Corrigez-la avec des périodes d'évaluation plus longues ou `DatapointsToAlarm` sur l'alarme elle-même.
- **Pas de synchronisation bidirectionnelle.** Corriger la cause n'efface rien sur votre téléphone.

Si ce sont des blocages, il vous faut une plateforme d'astreinte complète. Si ce qu'il vous faut, c'est « appelle-moi quand l'alarme de production se déclenche », ce sont quelques écrans de console.

## Dépannage

**L'alarme s'est déclenchée mais rien n'est arrivé.** Ouvrez l'onglet **Monitoring** de la règle : `Invocations` montre si le motif a correspondu, `FailedInvocations` si la livraison a échoué. Une règle située dans une autre région que l'alarme ne correspond jamais.

**La règle correspond mais l'appel échoue avec une erreur 400.** Le transformateur a produit du JSON invalide. Cherchez un guillemet ou une barre oblique inverse dans le nom ou la raison de l'alarme.

**La notification est vide.** Le modèle lit un champ que le transformateur n'a pas envoyé. Le corps est plat : `{{alarm}}`, pas `{{detail.alarmName}}`.

**Ça sonne en boucle pour une même alarme.** L'alarme est instable. Allongez la période d'évaluation ou exigez plus de points de données ; c'est un réglage CloudWatch, pas Echobell.

**Ça sonne pour une alarme inattendue.** Vérifiez qu'il ne manque pas un filtre `prefix` dans le motif, et rappelez-vous que les alarmes composites et celles créées par d'autres outils (les politiques Auto Scaling, par exemple) émettent aussi des changements d'état.

## Questions fréquentes

### CloudWatch peut-il appeler un numéro de téléphone nativement ?

Non. Les actions d'alarme CloudWatch sont des notifications SNS, Auto Scaling, des actions EC2 et Systems Manager. Un appel vocal exige un service externe : une Lambda qui compose via un fournisseur, une plateforme d'astreinte, ou un webhook d'appel comme Echobell.

### Ai-je besoin d'un topic SNS ?

Non. CloudWatch émet de lui-même les événements de changement d'état d'alarme vers EventBridge, que l'alarme ait des actions ou non. Vous pouvez laisser vide la liste d'actions de l'alarme.

### Une alarme CloudWatch traverse-t-elle Ne pas déranger ?

Seulement si elle arrive comme alerte de type appel. Un canal Echobell de type appel se comporte comme un appel entrant, que le mode Concentration et Ne pas déranger d'iOS laissent passer. Un SMS ou un push ordinaire, non.

### Pourquoi ne pas simplement abonner l'URL du webhook à SNS ?

SNS envoie du `text/plain` avec l'alarme sérialisée dans une chaîne `Message` : le modèle ne peut donc pas lire les champs individuels, et l'enveloppe est assez longue pour être tronquée. SNS envoie aussi d'abord une confirmation d'abonnement, qui arriverait comme notification.

### Est-ce que ça coûte quelque chose sur AWS ?

Les événements de changement d'état d'alarme arrivent sur le bus d'événements default sans frais ; les invocations d'API destination sont facturées à la requête. Au volume d'un jeu d'alarmes sain, c'est négligeable, mais consultez la tarification EventBridge actuelle pour votre région.

### Puis-je appeler des personnes différentes selon les alarmes ?

Oui. Créez un canal par audience, une règle par canal, et laissez chacun s'abonner avec le type de notification voulu. Il n'y a pas d'acquittement : toutes les personnes abonnées avec « appel » sont donc appelées.

### Et les alarmes dans plusieurs régions ou comptes ?

Les règles sont par région : créez la règle dans chaque région utilisée (la même définition d'API destination est répétée par région). Pour plusieurs comptes, envoyez les événements vers un bus central dans un compte et placez-y la règle.

## Pour finir

Le chemin tient en cinq parties : un canal d'appel, une connexion et une API destination qui pointent vers lui, une règle qui ne retient que les alarmes pour lesquelles on réveille quelqu'un, un transformateur d'entrée qui aplatit l'événement, et un test forcé avec `set-alarm-state` avant de vous fier à quoi que ce soit. Tout le reste ici sert à le garder assez étroit pour que, dans un mois, une sonnerie veuille encore dire quelque chose.

[Téléchargez Echobell pour iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-fr&mt=8) ou [obtenez-le sur Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), puis lancez le test `set-alarm-state`.

## Articles connexes

- [Référence des conditions de canal](/fr/docs/conditions)
- [Guide d'intégration des webhooks](/fr/docs/webhook)
- [Guide du déclencheur e-mail](/fr/docs/email-trigger)
- [Alertes Sentry par appel](/fr/blog/sentry-phone-call-alerts)
- [Alertes par appel pour les pannes d'API](/fr/blog/phone-call-alerts-api-downtime)
- [Guide du développeur contre la fatigue des alertes](/fr/blog/fix-alert-fatigue-developer-guide)
- [Contourner le mode Concentration d'iOS pour les alertes critiques](/fr/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)
