El 12 de noviembre de 2026 a las 16:17 UTC, usuarios de varias regiones dejaron de poder abrir sus buzones de Exchange Online. Buena parte de lo que depende de Exchange cayó con él: los calendarios y la programación de reuniones de Teams, partes de SharePoint y Copilot, las llamadas a Microsoft Graph de correo, calendario y contactos, y piezas del stack de seguridad. Microsoft lo siguió como incidente EX1550543, más MO1550821 para los servicios que dependen de Exchange Online, y a la mañana siguiente seguía restaurando servidores región por región.
No puedes arreglar una caída en la nube de otro. Lo que sí controlas es cuánto te cuesta: lo rápido que te enteras, lo que hacen tus usuarios mientras esperan, si un atacante aprovecha la confusión y lo que tienes preparado antes de la próxima. Este post cubre esas cuatro cosas, con un pequeño monitor que probamos y que te avisa por un canal que no depende de Microsoft 365.
Lo que sabemos de esta caída
Estado a 13 de noviembre de 2026, por la mañana (UTC): los dos incidentes siguen abiertos y Microsoft no ha publicado la causa raíz. Sus actualizaciones decían:
- Un proceso de los servidores afectados que establece las conexiones seguras se saturó. Esos servidores dejaron de responder y la carga cayó sobre los demás.
- Empezó a revertir un cambio reciente de configuración de red que coincide con el inicio del impacto. Es una coincidencia en el tiempo, no una causa confirmada.
- Pausó los despliegues mientras trabajaba en la recuperación y dijo que no había indicios de que la caída fuera resultado de actividad maliciosa.
- El correo entrante se retrasa, no se pierde.
Actualizaremos este post cuando salga el informe posterior al incidente. Si tu tenant se vio afectado, guarda ya una copia del incidente desde Service health: el historial solo cubre 30 días.
Los primeros 15 minutos: ¿eres tú o es Microsoft?
El peor uso de la primera hora es depurar tu propia red mientras el problema está en el centro de datos del proveedor. Antes de que nadie cambie nada:
- Mira Service health en el centro de administración de Microsoft 365 (Health y después Service health). Un incidente activo aparece entre los problemas en los que Microsoft está trabajando, con un ID como EX1550543; ábrelo y sigue las actualizaciones. Los administradores de Helpdesk y de Service Support también pueden verlo, así que quien responde primero no necesita un Global Administrator.
- Si no puedes entrar en el centro de administración, usa la página pública de estado en status.cloud.microsoft, que existe justo para este caso.
- Si todavía no aparece nada, prueba desde fuera de tu red, por ejemplo desde un móvil con datos. Si también falla ahí, repórtalo con "Report an issue" en la página de Service health. En los incidentes grandes, el aviso oficial suele llegar después de las primeras quejas de usuarios.
- Anota la hora de inicio que viste, los servicios afectados y el número de usuarios. Lo necesitarás para tu registro del incidente y para cualquier reclamación de créditos de servicio.
Avisa a la gente por un canal que siga funcionando
Si la única forma de llegar a tu equipo es el email o Teams, no tienes forma de llegar a él. Manda un mensaje corto por algo que esté fuera de Microsoft 365: SMS, una página de estado alojada en otro sitio, un grupo de mensajería creado de antemano. Di tres cosas:
- Qué está caído, y que es una caída del proveedor, no algo que hicieron ellos.
- Qué usar mientras tanto: el teléfono, el chat alternativo acordado, el correo que ya está en la caché de Outlook para leer.
- Qué no hacer: no reinstalar Office, no borrar el perfil de Outlook, no cambiar la contraseña. Nada de eso ayuda, y cada una de esas cosas genera trabajo al soporte durante días después de que vuelva el servicio.
Después publica actualizaciones a horas fijas, aunque no haya cambiado nada. El silencio hace que la gente improvise.
Las trampas de seguridad que abre una caída
Una caída es un evento de seguridad aunque nadie haya atacado a nadie. Las defensas en las que confías pueden estar degradadas, tus usuarios están confundidos y esperan mensajes raros, y tus administradores están bajo presión para hacer algo.
- Phishing que usa la caída como pretexto. Espera mensajes como "tu buzón se ha visto afectado, inicia sesión aquí para restaurarlo" durante el incidente y en los días siguientes. Añade una línea a tu mensaje sobre la caída: TI nunca te pedirá que inicies sesión a través de un enlace para recuperar tu buzón, y cualquier mensaje que lo haga hay que reportarlo.
- La detección pudo estar en parte ciega. Entre los servicios que figuraban como afectados estaban Safe Links de Defender for Office 365, que podía no conseguir verificar URLs, y los portales de Defender XDR y Purview. Lo que llegó o se pulsó en esa ventana merece una segunda mirada cuando vuelva el servicio.
- No improvises cambios de acceso. Desactivar el MFA o excluir a todo el mundo de una política de Conditional Access para "arreglar el inicio de sesión" no sirve de nada cuando el problema está en los servidores de correo del proveedor, y la excepción suele sobrevivir al incidente. Las emergencias de inicio de sesión se resuelven con lo que preparaste antes: políticas de Conditional Access de contingencia, en modo report-only, que activas en una emergencia real, y dos o más cuentas de acceso de emergencia solo en la nube, con FIDO2 o autenticación basada en certificados, validadas al menos cada 90 días.
- Atajos en la sombra. Cuando el correo cae, la gente reenvía trabajo a cuentas personales y comparte ficheros con lo que tenga a mano. Dales una alternativa autorizada antes de que encuentren la suya y revisa después qué salió por la puerta de atrás.
- Congela tus propios cambios. Microsoft pausó sus despliegues durante el incidente; haz lo mismo. Un cambio hecho durante una caída enturbia el diagnóstico y resta fiabilidad a tu propio registro del incidente.
Si usas Defender for Office 365, esta consulta de advanced hunting lista las URLs pulsadas desde el correo, Teams y las apps de Office dentro de la ventana del incidente, agrupadas para revisarlas. Ajusta la ventana a las horas que observaste:
UrlClickEvents
| where Timestamp between (datetime(2026-11-12T16:00:00Z) .. datetime(2026-11-13T12:00:00Z))
| summarize Clicks = count(), Users = dcount(AccountUpn), Actions = make_set(ActionType)
by Url, ThreatTypes
| order by Users descEmpieza por las URLs que pulsaron varios usuarios y por todo lo que ya tenga un veredicto de amenaza. Después contrasta el resto con lo que dicen hoy tus herramientas: un enlace que no se pudo verificar durante la caída puede tener veredicto ahora.
El correo se retrasa, pero ¿y tus aplicaciones?
El correo entrante aguanta bien una caída, porque SMTP se diseñó para eso. Un servidor emisor que no consigue entregar guarda el mensaje y lo reintenta, y el estándar espera que los emisores sigan intentándolo durante al menos cuatro o cinco días. Por eso el correo de este incidente se retrasó en lugar de perderse.
Tus propias aplicaciones son otra historia. Todo lo que envía a través de Microsoft Graph o SMTP AUTH (formularios de contacto, facturas, emails de restablecimiento de contraseña, notificaciones de alertas) falla en el momento en que intenta enviar. Si responde con un error y sigue adelante, el mensaje, y lo que escribió el usuario, se pierde. Durante este incidente, las peticiones de Graph de correo, calendario y contactos figuraban como susceptibles de agotar el tiempo de espera.
La solución es corriente y barata:
- Primero guarda, después envía. Escribe el envío en tu propia base de datos o cola, confirma al usuario y envía desde un worker.
- Reintenta con backoff, durante horas y no segundos, y alerta cuando la cola crezca.
- Pon un timeout a cada llamada al proveedor, para que una petición colgada no retenga indefinidamente a un worker ni a un usuario.
- No mandes alertas operativas a través del tenant que estás vigilando. Si las alertas de guardia llegan por Outlook, llegan después de la caída.
Vigila Microsoft 365 desde fuera de Microsoft 365
Service health puede enviarte emails, pero el correo de Microsoft 365 puede ser justo lo que no puedes leer. Los mismos incidentes están disponibles en Microsoft Graph, así que puedes llevarlos a cualquier sitio. El script de abajo consulta la API de incidencias de service health y publica en un webhook de chat: Slack, Mattermost, Google Chat o cualquier otro que acepte un campo JSON text. Avisa de:
- un incidente nuevo en los servicios que indiques;
- un cambio de estado o una actualización nueva en un incidente abierto;
- un incidente que se cierra;
- que el propio monitor no consiga llegar a Graph o al endpoint de inicio de sesión tres ejecuciones seguidas, porque en una caída grande eso puede ser lo primero que falle.
Necesita un registro de aplicación con el permiso de aplicación ServiceHealth.Read.All de Microsoft Graph, concedido por un administrador, y un client secret. En producción es mejor una credencial de certificado que un secret; el secret hace el ejemplo más corto.
#!/usr/bin/env python3
"""Alert on Microsoft 365 service incidents through a channel that does not depend on Microsoft 365.
Python 3.9+, standard library only. Run it every five minutes from cron or a systemd timer.
Needs an app registration with the Microsoft Graph application permission ServiceHealth.Read.All
(admin consent granted) and a client secret. Reads the tenant's service health issues, alerts on
new incidents, on status changes and new posts, and on incidents that close. If Graph or the sign-in
endpoint cannot be reached several runs in a row, that is an alert too: in a large outage, the
monitor's own dependencies may be the first thing to fail.
"""
import html
import json
import os
import re
import sys
import urllib.parse
import urllib.request
TENANT_ID = os.environ["TENANT_ID"]
CLIENT_ID = os.environ["CLIENT_ID"]
CLIENT_SECRET = os.environ["CLIENT_SECRET"]
WEBHOOK_URL = os.environ["WEBHOOK_URL"] # Slack, Mattermost, Google Chat... not Teams, not email in Microsoft 365
SERVICES = {s.strip().lower() for s in os.environ.get("SERVICES", "").split(",") if s.strip()}
ADVISORIES = os.environ.get("INCLUDE_ADVISORIES") == "1"
STATE_FILE = os.environ.get("STATE_FILE", "/var/lib/m365-health/state.json")
FAILS_BEFORE_ALERT = int(os.environ.get("FAILS_BEFORE_ALERT", "3"))
LOGIN_URL = os.environ.get("LOGIN_URL", "https://login.microsoftonline.com")
GRAPH_URL = os.environ.get("GRAPH_URL", "https://graph.microsoft.com/v1.0")
def request(url, data=None, headers=None):
req = urllib.request.Request(url, data, headers or {})
with urllib.request.urlopen(req, timeout=30) as resp:
return json.load(resp)
def fetch_issues():
form = urllib.parse.urlencode({
"grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET,
"scope": "https://graph.microsoft.com/.default"}).encode()
token = request(f"{LOGIN_URL}/{TENANT_ID}/oauth2/v2.0/token", form)["access_token"]
issues, url = [], f"{GRAPH_URL}/admin/serviceAnnouncement/issues"
while url: # the list is paged; follow every @odata.nextLink
page = request(url, headers={"Authorization": f"Bearer {token}"})
issues += page.get("value", [])
url = page.get("@odata.nextLink")
return issues
def relevant(issue):
if issue.get("isResolved"):
return False
if str(issue.get("classification", "")).lower() != "incident" and not ADVISORIES:
return False
return not SERVICES or str(issue.get("service", "")).lower() in SERVICES
def latest_post(issue):
posts = sorted(issue.get("posts") or [], key=lambda p: p.get("createdDateTime", ""))
if not posts:
return ""
text = html.unescape(re.sub(r"<[^>]+>", " ", posts[-1].get("description", {}).get("content", "")))
return " ".join(text.split())[:400]
def describe(event, issue):
return (f"[M365] {event}: {issue['id']} | {issue.get('service')} | {issue.get('status')}\n"
f"{issue.get('title', '')}\n{latest_post(issue)}").strip()
def send(text):
request(WEBHOOK_URL, json.dumps({"text": text}).encode(), {"Content-Type": "application/json"})
def main():
try:
with open(STATE_FILE) as f:
state = json.load(f)
except FileNotFoundError:
state = {"open": {}, "fails": 0, "fail_alerted": False}
messages = []
try:
everything = {i["id"]: i for i in fetch_issues()}
except Exception as exc: # sign-in endpoint, Graph or the network
state["fails"] += 1
if state["fails"] >= FAILS_BEFORE_ALERT and not state["fail_alerted"]:
messages.append(f"[M365] Monitor cannot reach Microsoft Graph, {state['fails']} runs in a row: {exc}")
state["fail_alerted"] = True
everything = None
else:
if state["fail_alerted"]:
messages.append("[M365] Monitor reaches Microsoft Graph again")
state["fails"], state["fail_alerted"] = 0, False
current = {k: i for k, i in everything.items() if relevant(i)}
for key, issue in current.items():
mark = f"{issue.get('status')}|{issue.get('lastModifiedDateTime')}"
if key not in state["open"]:
messages.append(describe("NEW", issue))
elif state["open"][key] != mark:
messages.append(describe("UPDATE", issue))
for key in sorted(state["open"].keys() - current.keys()):
closed = everything.get(key, {"id": key, "service": "-", "status": "no longer listed"})
messages.append(describe("CLOSED", closed))
state["open"] = {k: f"{i.get('status')}|{i.get('lastModifiedDateTime')}" for k, i in current.items()}
try:
for text in messages:
send(text)
except Exception as exc:
print(f"alert delivery failed, state not saved so the next run retries: {exc}", file=sys.stderr)
return 2
os.makedirs(os.path.dirname(os.path.abspath(STATE_FILE)), exist_ok=True)
with open(STATE_FILE + ".tmp", "w") as f:
json.dump(state, f)
os.replace(STATE_FILE + ".tmp", STATE_FILE)
return 1 if everything is None else 0
if __name__ == "__main__":
sys.exit(main())Ejecútalo cada cinco minutos desde una máquina que no dependa de Microsoft 365, con el secret en un fichero de entorno que solo pueda leer la cuenta de servicio:
# /etc/m365-health.env (chmod 600, owned by the account that runs the monitor;
# that account must be able to write the STATE_FILE directory)
TENANT_ID='00000000-0000-0000-0000-000000000000'
CLIENT_ID='11111111-1111-1111-1111-111111111111'
CLIENT_SECRET='your-client-secret'
WEBHOOK_URL='https://chat.example.com/hooks/your-webhook-id'
SERVICES='Exchange Online,Microsoft Teams,SharePoint Online,Microsoft 365 suite'
STATE_FILE='/var/lib/m365-health/state.json'
# crontab of that account: every five minutes
*/5 * * * * set -a; . /etc/m365-health.env; set +a; /usr/bin/python3 /opt/m365-health/m365_health_watch.pyLo probamos contra una simulación del endpoint de inicio de sesión y de la API de incidencias, construida a partir del formato de respuesta documentado y reproduciendo los IDs de este incidente. Los 15 pasos incluyeron un incidente que solo aparece en la segunda página de resultados, un estado enviado como ServiceDegradation en una incidencia y como serviceDegradation en otra (la documentación muestra las dos formas), un webhook caído y tres ejecuciones fallidas seguidas contra el endpoint de inicio de sesión y Graph. Con el webhook caído, el script terminó con error sin guardar su estado, y la ejecución siguiente entregó la alerta pendiente en lugar de perderla. Dos copias rotas a propósito, una que comparaba la clasificación distinguiendo mayúsculas y otra que ignoraba la segunda página, no vieron el incidente de Exchange y fallaron el test. Las alertas tenían este aspecto:
[M365] NEW: EX1550543 | Exchange Online | ServiceDegradation
Users may be unable to access their mailbox through multiple connection methods
Title: Users may be unable to access their mailbox
[M365] UPDATE: EX1550543 | Exchange Online | RestoringService
Users may be unable to access their mailbox through multiple connection methods
We've begun reverting a recent network configuration change & restarting
[M365] Monitor cannot reach Microsoft Graph, 3 runs in a row: HTTP Error 503: Service Unavailable
[M365] Monitor reaches Microsoft Graph again
[M365] CLOSED: EX1550543 | Exchange Online | ServiceRestored
Users may be unable to access their mailbox through multiple connection methods
We've begun reverting a recent network configuration change & restartingPrepárate antes de la próxima
No fue el primer gran incidente de Microsoft 365 y no será el último. Lo que conviene tener listo:
- Avisos de Service health que te lleguen. En Service health, Customize y después Email envía notificaciones de incidentes nuevos y cambios de estado a hasta dos direcciones por administrador. Haz que al menos una sea una dirección fuera de Microsoft 365, instala la app Microsoft 365 Admin en los móviles de guardia y ejecuta un monitor como el de arriba.
- Una lista de contactos y un runbook que puedas leer con Microsoft 365 caído. Si el plan para caídas vive en SharePoint o OneNote, nadie puede abrirlo cuando importa. Guarda una copia en otro sitio, y los teléfonos en papel.
- Un canal alternativo acordado, creado y probado antes del día en que lo necesites, con las personas que lo van a usar ya dentro.
- Cuentas de acceso de emergencia: al menos dos, solo en la nube, en el dominio onmicrosoft.com, con autenticación resistente al phishing distinta de la de tus administradores habituales, excluidas de las políticas de Conditional Access que bloquean el inicio de sesión, con una alerta en cada uso y probadas al menos cada 90 días.
- Aplicaciones que encolan en lugar de fallar, como se explica arriba.
- Un ejercicio de mesa: "Exchange Online está caído durante una jornada laboral". Recorre quién se entera, quién decide, qué se dice a los usuarios y qué procesos de negocio se paran. Una hora alrededor de una mesa encuentra los huecos que, si no, encontrarías en directo.
Los backups son otro plan: te protegen de perder datos, no de no poder llegar a ellos. Para el lado de la pérdida de datos, mira cómo montar backups que sobreviven al ransomware.
Cuando vuelva el servicio
- Guarda el informe posterior al incidente desde el incidente en Service health cuando Microsoft lo publique. El historial solo cubre 30 días.
- Registra tu propio impacto: inicio y fin tal como los viste, los equipos afectados, los atajos que se usaron y lo que se perdió. Ese registro es lo que hace que el próximo plan sea mejor que este.
- Reclama el crédito de servicio si te corresponde. Los créditos del SLA de Microsoft no se aplican solos: un administrador tiene que presentar la reclamación desde el centro de administración y, para los servicios de Microsoft 365, tiene que llegar antes de que acabe el mes natural siguiente al incidente. Comprueba el SLA vigente de los servicios que contratas.
- Elimina las excepciones temporales. Cualquier cambio de políticas, regla de reenvío o enlace compartido creado durante la caída se revisa y se elimina.
- Ejecuta la consulta de hunting y revisa los reportes de phishing de esa ventana.
Nuestro NOC 24/7 añade comprobaciones del proveedor como esta junto a tus propias alertas y las vigila a todas horas, para que te enteres de una caída antes que tus clientes. Nuestro equipo de ciberseguridad cubre la otra mitad: el phishing, los cambios de acceso y la revisión posterior.
Evaluación gratuita de preparación ante incidentes
Si esta caída pilló a tu equipo sin plan, lo revisamos contigo:
- Detección: cómo te enterarías de la próxima caída del proveedor y si esos avisos te llegan con Microsoft 365 caído.
- Respuesta: el canal alternativo, los mensajes a los usuarios, las cuentas de acceso de emergencia y las políticas de acceso de contingencia.
- Dependencias: qué aplicaciones y procesos se paran cuando el correo, el calendario o el inicio de sesión no están disponibles, y cuáles pierden datos.
Recibes por escrito una lista de los huecos y la solución para cada uno, sin ningún compromiso.
Solicita tu evaluación gratuita de preparación ante incidentes, o descubre nuestras operaciones de NOC 24/7.