Cuando el vuelo CA-1187 se retrasa tres horas, Contoso Airlines tiene que hacer siempre lo mismo: buscar en db-reservas los pasajeros afectados, enviarles un correo y un mensaje de texto, publicar el aviso en el canal del equipo de operaciones y abrir un incidente en el sistema de soporte. Nada de eso es lógica de negocio compleja; es pegar sistemas entre sí. Escrito como código serían cuatro integraciones con sus autenticaciones, sus formatos y sus reintentos, mantenidas por Diego Salas. Y hay un detalle incómodo: quien conoce el proceso de verdad es el equipo de operaciones, que no programa.

Azure Logic Apps existe para eso. Es un motor de flujos de trabajo con más de mil conectores listos, que se diseña visualmente y se guarda como una definición JSON versionable. Esta lección enseña a construir ese flujo real, a gestionar sus errores, a desplegarlo entre entornos —donde está el problema clásico, las conexiones— y a saber cuándo toca una aplicación lógica y cuándo no.

Contenido

  1. Qué es una aplicación lógica y en qué se diferencia de Functions
  2. Consumo frente a Estándar
  3. Anatomía: desencadenador, acciones y conectores
  4. El flujo de retraso de vuelo, paso a paso
  5. La definición JSON subyacente
  6. Control de flujo: condiciones, bucles, ámbitos y paralelismo
  7. Gestión de errores: reintentos, ejecución posterior y compensación
  8. Conexiones y su autenticación
  9. Supervisión y depuración de una ejecución fallida
  10. Integración con el resto de la plataforma
  11. Despliegue con Bicep y el problema de las conexiones
  12. Coste por acción ejecutada
  13. Logic Apps, Functions o Data Factory
  14. Errores Comunes y Consejos
  15. Ejercicios
  16. Conclusión

  1. Qué es una aplicación lógica y en qué se diferencia de Functions

Una aplicación lógica es un flujo de trabajo: un desencadenador y una secuencia de acciones conectadas, donde la salida de cada paso alimenta al siguiente. No compilas nada ni gestionas dependencias.

Azure Functions Azure Logic Apps
Cómo se construye Escribiendo código Diseñador visual (+ JSON)
Quién lo mantiene Desarrolladores Desarrolladores y perfiles de negocio
Punto fuerte Lógica y transformación a medida Conectar sistemas ya existentes
Integrarse con SaaS Escribir el cliente HTTP Conector listo
Coste Por ejecución y GB-s Por acción ejecutada
Depuración Registros y trazas Historial visual con datos de cada paso
Cuándo brilla Cálculo, algoritmo, transformación compleja Orquestación entre servicios y esperas humanas

La regla es simple: si el problema es «hacer algo complicado», Functions; si el problema es «hablar con seis sistemas distintos», Logic Apps. Y no compiten: el flujo de Contoso llamará a una función de 06-03 para la parte que sí requiere código.

  1. Consumo frente a Estándar

Consumo Estándar
Motor Multiinquilino compartido Dedicado, sobre el tiempo de ejecución de Functions
Precio Por acción ejecutada Por plan de hospedaje (fijo)
Flujos por recurso Uno Varios en la misma aplicación
Integración con red virtual No (requiere ISE) Sí, con puntos privados
Ejecución y depuración en local No Sí, con VS Code
Conectores integrados Pocos Muchos más, sin coste por acción
Cuándo Flujos esporádicos y sencillos Producción, alto volumen, red privada

Contoso elige Estándar para logic-contoso-retrasos-pro por dos razones concretas: necesita alcanzar pe-sql-reservas por punto privado, y con volumen alto el precio fijo es más predecible que pagar por acción cuando un solo retraso puede desencadenar cientos de acciones.

  1. Anatomía: desencadenador, acciones y conectores

  • Desencadenador: qué inicia el flujo. Puede ser una petición HTTP, una programación, o un conector que sondea un sistema («cuando llegue un correo», «cuando se cree una fila»).
  • Acción: cada paso posterior. Consulta datos, llama a una API, envía un mensaje, decide, itera.
  • Conector integrado: se ejecuta dentro del motor. Rápido, sin coste por acción en el nivel Estándar: HTTP, Service Bus, Azure Functions, SQL Server, control de flujo.
  • Conector gestionado: un envoltorio alojado por Microsoft sobre un servicio externo: Office 365, Salesforce, SAP, Twilio, ServiceNow, Slack. Se factura por acción y requiere una conexión con sus credenciales.

El valor real de Logic Apps es ese catálogo de más de mil conectores. Integrar ServiceNow a mano son días de trabajo —autenticación, esquema, reintentos, paginación— y aquí es arrastrar una acción. Ese es todo el argumento, y es un argumento fuerte.

  1. El flujo de retraso de vuelo, paso a paso

graph TD
  T["Desencadenador HTTP:<br/>POST /retraso {vuelo, minutos}"] --> V{"¿minutos > 60?"}
  V -->|No| FIN["Registrar y terminar"]
  V -->|Si| SQL["SQL: pasajeros afectados<br/>de db-reservas"]
  SQL --> FE["Para cada pasajero (en paralelo)"]
  FE --> COR["Office 365: correo"]
  FE --> SMS["Twilio: mensaje"]
  SQL --> OPS["Teams: aviso al canal<br/>de operaciones"]
  SQL --> INC["ServiceNow: abrir incidente"]
  SQL --> FUN["Function CalcularCompensacion<br/>(06-03)"]
  FUN --> INC

Los pasos, en orden:

  1. Desencadenador «Cuando se recibe una petición HTTP». Genera una URL con una firma; el sistema de operaciones publica ahí { "vuelo": "CA-1187", "minutos": 180, "fecha": "2026-08-15" }. Se define un esquema JSON de la carga para que los campos aparezcan como fichas seleccionables en los pasos siguientes.
  2. Condición: si el retraso no supera 60 minutos, no hay obligación de notificar. Se registra y termina.
  3. Acción SQL «Ejecutar consulta» contra db-reservas, con el número de vuelo y la fecha como parámetros —nunca concatenados en el texto de la consulta, que sería inyección SQL—, devolviendo localizador, nombre, correo y teléfono.
  4. Llamar a la función CalcularCompensacion de 06-03 con el retraso y la ruta. Ese cálculo depende del reglamento europeo, tiene sus casos especiales y es código: no se hace con acciones visuales.
  5. Bucle for each sobre los pasajeros, en paralelo, con el envío de correo (Office 365) y de mensaje (Twilio) dentro.
  6. Aviso al canal de operaciones en Teams, con el resumen y el número de afectados.
  7. Abrir el incidente en ServiceNow con el importe de compensación calculado.

  1. La definición JSON subyacente

El diseñador es una vista sobre un fichero. Esto importa: la aplicación lógica es código versionable que vive en contoso-infra y pasa por revisión como cualquier cambio (05-02).

{
  "definition": {
    "triggers": {
      "PeticionRetraso": {
        "type": "Request", "kind": "Http",
        "inputs": { "schema": { "type": "object", "properties": {
          "vuelo":   { "type": "string" },
          "minutos": { "type": "integer" },
          "fecha":   { "type": "string" } } } }
      }
    },
    "actions": {
      "SiSuperaUnaHora": {
        "type": "If",
        "expression": { "greater": [ "@triggerBody()?['minutos']", 60 ] },
        "actions": {
          "ObtenerPasajeros": {
            "type": "ApiConnection",
            "inputs": {
              "host": { "connection": { "name": "@parameters('$connections')['sql']['connectionId']" } },
              "method": "post", "path": "/datasets/default/query/sql",
              "body": {
                "query": "SELECT localizador, nombre, correo, telefono FROM Reservas WHERE numeroVuelo=@vuelo AND fecha=@fecha",
                "parameters": { "vuelo": "@triggerBody()?['vuelo']",
                                "fecha": "@triggerBody()?['fecha']" } }
            },
            "runAfter": {}
          },
          "PorCadaPasajero": {
            "type": "Foreach",
            "foreach": "@body('ObtenerPasajeros')?['resultsets']['Table1']",
            "runtimeConfiguration": { "concurrency": { "repetitions": 20 } },
            "actions": { "EnviarCorreo": { "type": "ApiConnection", "inputs": { } } },
            "runAfter": { "ObtenerPasajeros": [ "Succeeded" ] }
          }
        }
      }
    }
  }
}

Tres piezas que conviene reconocer:

  • @triggerBody()?['minutos'] es el lenguaje de expresiones. El ? es el acceso seguro: si el campo no viene, devuelve nulo en lugar de romper el flujo. Úsalo siempre con datos externos.
  • runAfter es lo que define el orden real de ejecución, no la posición visual. Un paso con runAfter vacío arranca en cuanto la rama empieza; dos acciones con el mismo runAfter se ejecutan en paralelo.
  • concurrency.repetitions limita cuántas iteraciones del bucle corren a la vez. Sin ese límite, un vuelo de 300 pasajeros dispara 300 llamadas simultáneas a Twilio y provoca limitación de solicitudes.

  1. Control de flujo: condiciones, bucles, ámbitos y paralelismo

  • Condición (If) con dos ramas, y Switch cuando hay varios casos discretos, como el tipo de incidencia.
  • For each: itera sobre una colección, por defecto en paralelo con 20 repeticiones simultáneas. Si el orden importa, hay que forzar la concurrencia a 1.
  • Until: repite hasta que se cumple una condición o se agota un contador. Es el patrón de sondeo —«consultar el estado del reembolso cada 5 minutos hasta que esté aprobado o pasen 2 horas»—. Pon siempre un límite de iteraciones y de tiempo, o tienes un bucle infinito facturable.
  • Ámbito (Scope): agrupa acciones en un bloque que tiene su propio estado agregado. Es la base de la gestión de errores del apartado siguiente: funciona como un try.
  • Ramas paralelas: dos acciones con el mismo runAfter se ejecutan a la vez. Contoso lo usa para que el aviso a Teams y el incidente de ServiceNow no esperen al bucle de pasajeros.

  1. Gestión de errores: reintentos, ejecución posterior y compensación

Tres capas, de la más automática a la más manual.

Directiva de reintentos. Cada acción la lleva y por defecto reintenta 4 veces con espera exponencial ante errores transitorios (429 y 5xx). Se ajusta por acción:

"retryPolicy": { "type": "exponential", "count": 5,
                 "interval": "PT10S", "maximumInterval": "PT1H" }

Con "type": "none" se desactiva, y hay un caso en que hay que desactivarlo: cuando la acción no es idempotente. Reintentar un cargo en la pasarela de pago cobra dos veces. Es la misma lección de 06-03, aquí en versión visual.

Acciones de ejecución posterior. El runAfter puede condicionarse a Failed, TimedOut o Skipped, no solo a Succeeded. Ese es el catch:

"NotificarFalloIntegracion": {
  "type": "ApiConnection",
  "runAfter": { "AmbitoNotificaciones": [ "Failed", "TimedOut" ] },
  "inputs": { }
}

Combinado con un ámbito, el patrón try/catch queda: ámbito AmbitoNotificaciones con los envíos dentro, y una acción posterior que se ejecuta solo si el ámbito falló, avisando al equipo con @result('AmbitoNotificaciones'), que devuelve el detalle de qué acción concreta reventó.

Compensación. No hay transacciones distribuidas: si el correo se envió y el mensaje falló, el correo no se puede «deshacer». El patrón es añadir acciones que corrigen —marcar el aviso como parcial, encolar un reenvío, abrir un incidente de revisión manual—. Diséñalo explícitamente, porque el flujo va a fallar a la mitad tarde o temprano.

  1. Conexiones y su autenticación

Una conexión es un recurso de Azure independiente del flujo que guarda cómo autenticarse contra un sistema. Y son de tres tipos:

Tipo Ejemplo Riesgo
Identidad administrada SQL Server, Blob, Key Vault, Service Bus Ninguno: sin secretos, y es la opción correcta
Entidad de servicio APIs de Azure con permisos concretos Secreto que rotar
OAuth delegado Office 365, Teams, Salesforce Atado a una persona: si se va, el flujo muere

Contoso usa identidad administrada siempre que el conector la admite: id-contoso-api-pro accede a db-reservas y a kv-contoso-pro sin credenciales. Para los conectores que exigen OAuth delegado, la regla del equipo es autorizarlos con una cuenta de servicio del dominio —[email protected]— y nunca con la cuenta personal de Marta o Diego. Es un error que se paga meses después, cuando alguien cambia de puesto y los flujos empiezan a fallar con 401.

  1. Supervisión y depuración de una ejecución fallida

El historial de ejecuciones es la mejor herramienta de diagnóstico de todo el módulo. Cada ejecución muestra el flujo dibujado con cada paso en verde o rojo y, al pinchar en un paso, las entradas y las salidas exactas de esa ejecución concreta: el JSON que entró, el que salió y el error literal.

El proceso de depuración práctico: abrir la ejecución fallida, localizar el primer paso en rojo —los siguientes suelen ser consecuencia—, leer sus entradas para ver qué datos recibió realmente, y contrastar con lo que esperabas. La mayoría de los fallos son de forma de los datos: un campo nulo que se asumía presente, una fecha en otro formato, una colección vacía. Corregido el problema en el sistema de origen, la reejecución relanza esa misma ejecución con los mismos datos de entrada, sin tener que reproducir el evento original.

Se completa con el envío de diagnósticos a log-contoso-pro para consultar con KQL y crear alertas sobre el número de ejecuciones fallidas (07-01 y 07-02).

  1. Integración con el resto de la plataforma

Aquí es donde Logic Apps deja de ser una herramienta suelta:

  • Desde una alerta de Azure Monitor (07-01): un grupo de acciones puede invocar la URL del desencadenador HTTP. Así, «la latencia de app-contoso-reservas-pro supera 2 s» deja de ser un correo y pasa a ser un flujo que abre el incidente, avisa al canal y adjunta el enlace al panel.
  • Desde Defender for Cloud (04-05): sus automatizaciones de flujo de trabajo disparan aplicaciones lógicas ante una alerta de seguridad, para aislar un recurso o notificar al responsable de guardia.
  • Desde Event Grid (06-05): reaccionar a eventos de la plataforma sin escribir nada.
  • Llamando a una función de 06-03 con el conector integrado de Azure Functions, para la lógica que sí es código. Esta combinación es la buena: Logic Apps orquesta, Functions calcula.

  1. Despliegue con Bicep y el problema de las conexiones

El flujo se despliega como cualquier otro recurso desde contoso-infra, con el pipeline de infraestructura de 05-06:

resource flujoRetrasos 'Microsoft.Logic/workflows@2019-05-01' = {
  name: 'logic-contoso-retrasos-${entorno}'
  location: ubicacion
  identity: { type: 'UserAssigned', userAssignedIdentities: { '${idIdentidad}': {} } }
  tags: etiquetasComunes
  properties: {
    state: 'Enabled'
    definition: json(loadTextContent('flujos/retrasos.json'))
    parameters: {
      '$connections': { value: { sql: {
        connectionId: conexionSql.id
        connectionName: conexionSql.name
        id: subscriptionResourceId('Microsoft.Web/locations/managedApis',
                                   ubicacion, 'sql') } } }
    }
  }
}

Y aquí está el problema clásico. La definición del flujo se promociona limpiamente entre entornos, pero las conexiones no: cada entorno tiene las suyas, con identificadores distintos, y las de OAuth delegado necesitan que un humano pulse «Autorizar» en el portal la primera vez. Si no se separan, el despliegue en producción arrastra la conexión de desarrollo y el flujo acaba consultando db-reservas del entorno equivocado. La disciplina que lo evita:

  1. La definición del flujo nunca lleva identificadores de conexión codificados: van en el parámetro $connections.
  2. Las conexiones se declaran como recursos aparte, por entorno, con su .bicepparam.
  3. Las de identidad administrada se automatizan del todo; las de OAuth se autorizan una vez con la cuenta de servicio del dominio y se documenta ese paso manual.

  1. Coste por acción ejecutada

En el nivel Consumo se paga cada acción ejecutada, y el matiz es que las iteraciones de un bucle cuentan por separado. Haz la cuenta del flujo de Contoso: un vuelo de 250 pasajeros ejecuta 2 acciones por pasajero, o sea 500, más una decena del resto. Cinco retrasos al día son 2.500 acciones diarias. Con céntimos por cada mil acciones parece nada, pero el patrón que dispara la factura sin avisar es siempre el mismo: un desencadenador de sondeo cada minuto —43.200 comprobaciones al mes aunque no pase nada—, o un Until mal acotado, o un bucle anidado que multiplica.

Las medidas: usar desencadenadores de notificación en lugar de sondeo cuando el sistema lo permite (webhook o Event Grid), espaciar el sondeo a lo que de verdad se necesita, acotar los Until con contador y tiempo máximo, y en volúmenes altos pasar al nivel Estándar, donde el coste es un plan fijo y los conectores integrados no se facturan por acción.

  1. Logic Apps, Functions o Data Factory

Logic Apps Azure Functions Data Factory (03-06)
Propósito Orquestar e integrar sistemas Ejecutar lógica a medida Mover y transformar datos a escala
Unidad de trabajo Un evento de negocio Un evento o petición Un lote de millones de filas
Construcción Diseñador visual + JSON Código Canalizaciones y flujos de datos
Conectores a SaaS Más de mil Los que escribas Muchos, orientados a datos
Esperas humanas Sí, nativas Con Durable Functions No
Caso de Contoso Avisar por retraso de vuelo Generar el PDF de la tarjeta Cargar stlagocontosopro cada noche

La frontera con Data Factory se resuelve con una pregunta: ¿el flujo trata un caso o un conjunto? Notificar a los pasajeros de un vuelo es un caso: Logic Apps. Cargar 40 millones de registros de embarque en la capa bronce es un conjunto: Data Factory.

Errores Comunes y Consejos

  • Autorizar conexiones OAuth con una cuenta personal. El día que esa persona cambia de puesto, los flujos fallan con 401. Cuenta de servicio del dominio.
  • Codificar identificadores de conexión en la definición. Rompe la promoción entre entornos. Parámetro $connections.
  • Concatenar valores en una consulta SQL. Inyección SQL de manual. Usa parámetros.
  • Dejar el for each con la concurrencia por defecto cuando llama a una API con límite. 300 llamadas simultáneas provocan limitación y fallos en cascada.
  • Reintentar acciones no idempotentes. Un cargo reintentado se cobra dos veces. Desactiva la directiva en esos pasos.
  • Un Until sin límite de iteraciones y de tiempo. Bucle infinito que además factura.
  • Sondear cada minuto por comodidad. Es la causa número uno de facturas sorpresa en el nivel Consumo.
  • Consejo: nombra las acciones con lo que hacen (ObtenerPasajerosAfectados), no con el nombre por defecto. Las expresiones referencian ese nombre y renombrar después las rompe.
  • Consejo: si el flujo tiene más de 30 acciones o mucha lógica condicional, la lógica pertenece a una función. El diseñador visual deja de ayudar cuando el flujo parece un plato de espaguetis.

Ejercicios

Ejercicio 1. Contoso Millas necesita un flujo mensual que calcule el nivel de fidelidad de cada socio, envíe un correo a quienes suben de nivel y publique el resumen en el canal del equipo comercial.

  1. Elige nivel (Consumo o Estándar) y desencadenador, y justifícalo.
  2. Enumera las acciones con su control de flujo, indicando dónde llamarías a una función y por qué.
  3. Indica las etiquetas obligatorias y estima el orden de magnitud del coste si hay 80.000 socios y 3.000 suben de nivel.

Ejercicio 2. Este flujo falla: la acción CargarPasaporte sube un documento al almacenamiento, ActualizarReserva marca el check-in en db-reservas, y EnviarConfirmacion avisa al pasajero. Ayer, ActualizarReserva falló por un tiempo de espera agotado y hoy hay pasaportes cargados de reservas que figuran sin check-in.

  1. Explica por qué falló, incluyendo qué hicieron los reintentos.
  2. Rediseña el flujo con ámbitos, ejecución posterior y compensación.
  3. ¿En qué se diferencia esto de una transacción y qué garantía puedes dar de verdad?

Ejercicio 3. Para cada caso elige Logic Apps, Functions o Data Factory: (a) al recibir un correo de un proveedor con un CSV adjunto, validarlo y abrir un incidente si tiene errores; (b) recalcular cada noche las tarifas de 12 millones de combinaciones origen-destino; (c) al confirmarse un pago, generar la factura en PDF con las reglas fiscales de siete países.

Soluciones

Solución 1:

  1. Consumo con desencadenador de programación mensual: se ejecuta una vez al mes, no necesita red virtual y el precio fijo del nivel Estándar no se justifica para doce ejecuciones al año.
  2. Programación → llamar a la función CalcularNiveles con el lote de socios → for each sobre los que suben de nivel, con concurrencia limitada, enviando el correo dentro → aviso a Teams con el resumen. El cálculo del nivel va en una función porque son reglas de negocio con umbrales, casos especiales y vigencias: eso es código, se prueba con pruebas unitarias y no se mantiene con acciones visuales. Además, procesar 80.000 socios paso a paso en el diseñador sería lentísimo y carísimo en acciones.
  3. entorno, proyecto=contoso-millas, centro-coste=CC-2077 y propietario. El coste depende de la clave del diseño: si la evaluación se hace dentro de la función, se facturan del orden de 3.000 iteraciones más una decena de acciones, es decir, unos pocos miles de acciones al mes: céntimos. Si en cambio se iterara sobre los 80.000 socios en el flujo con dos acciones cada uno, serían 160.000 acciones mensuales. La lección está ahí: el bucle en el sitio equivocado multiplica la factura por cincuenta.

Solución 2:

  1. ActualizarReserva agotó su tiempo de espera contra db-reservas. La directiva de reintentos por defecto lo reintentó cuatro veces con espera exponencial y, al seguir fallando, la acción quedó en Failed; como EnviarConfirmacion tenía runAfter: Succeeded, no se ejecutó y el flujo terminó fallido. Pero CargarPasaporte ya se había completado, y nada lo revirtió: de ahí los documentos huérfanos.
  2. Envolver CargarPasaporte y ActualizarReserva en un ámbito AmbitoCheckIn. Añadir una acción con runAfter: { AmbitoCheckIn: ["Failed","TimedOut"] } que ejecute la compensación: marcar el documento cargado como pendiente de conciliación, encolar un reintento diferido en cola-emision-tarjetas y abrir un incidente con @result('AmbitoCheckIn'). EnviarConfirmacion se mantiene con runAfter: Succeeded sobre el ámbito, para que el pasajero solo reciba la confirmación si todo salió bien.
  3. Una transacción daría atomicidad: o todo o nada, con reversión automática. Aquí no la hay, porque los sistemas son independientes y no comparten un coordinador. Lo que puedes garantizar es consistencia final con compensación: cada efecto parcial queda registrado y hay un camino explícito que lo corrige o lo escala a un humano. La diferencia práctica es que existe una ventana en la que el sistema es incoherente, y el diseño tiene que aceptarla y hacerla visible en lugar de fingir que no existe.

Solución 3: (a) Logic Apps: el desencadenador de correo, el adjunto y la apertura del incidente son tres conectores listos, y el flujo es de integración pura. (b) Data Factory: es un conjunto masivo procesado por lotes; hacerlo con acciones de flujo sería absurdo en coste y tiempo. (c) Functions: reglas fiscales de siete países más generación de PDF es lógica compleja y código probable; a lo sumo, una aplicación lógica invoca esa función como parte de un flujo mayor.

Conclusión

Sabes qué es una aplicación lógica y en qué se diferencia de una función: flujo visual frente a código, integrar frente a calcular, coste por acción frente a coste por ejecución, y que la combinación buena es que Logic Apps orquesta y Functions calcula. Distingues Consumo de Estándar por motor, precio, red virtual, número de flujos y ejecución local, y sabes por qué Contoso eligió Estándar para logic-contoso-retrasos-pro. Dominas la anatomía —desencadenador, acciones, conectores integrados frente a gestionados— y entiendes que el catálogo de más de mil conectores es el verdadero argumento del servicio.

Has construido el flujo real de Contoso de principio a fin: petición HTTP con esquema, condición sobre los 60 minutos, consulta parametrizada a db-reservas, llamada a una función para la compensación, bucle sobre los pasajeros con concurrencia limitada, aviso al canal de operaciones e incidente en el sistema de soporte. Y has leído su definición JSON, que es lo que convierte el diseñador en algo versionable en contoso-infra: @triggerBody()?[...] con acceso seguro, runAfter como orden real de ejecución y control explícito del paralelismo. Manejas el control de flujo completo —condiciones, Switch, for each, Until acotado, ámbitos y ramas paralelas— y las tres capas de gestión de errores: directivas de reintento (desactivadas donde la acción no es idempotente), acciones de ejecución posterior como catch, y compensación, porque aquí no hay transacciones. Sabes autenticar con identidad administrada siempre que se pueda y con una cuenta de servicio del dominio cuando toca OAuth delegado, depurar leyendo entradas y salidas reales en el historial y reejecutar, y disparar flujos desde Azure Monitor, Defender for Cloud y Event Grid. Y te llevas dos avisos concretos: el problema de las conexiones al promocionar entre entornos, con su disciplina de parámetros y recursos separados, y el coste por acción, que se dispara con el sondeo cada minuto y los bucles mal colocados.

Queda algo más profundo, y es el sustrato de todo lo anterior. Cada vez que Contoso confirma una reserva, hoy pasan seis cosas en línea, dentro de la misma petición: se cobra, se actualiza la disponibilidad, se emite la tarjeta, se acreditan las millas, se avisa a facturación y se envía la confirmación. Si el servicio de millas está caído, la compra falla. Es una arquitectura acoplada, y ni Functions ni Logic Apps la arreglan por sí solos: hace falta que los componentes dejen de llamarse directamente y empiecen a intercambiar mensajes y eventos. La lección siguiente, Mensajería y eventos: Service Bus, Event Grid y Event Hubs, ataca eso de frente: la distinción entre mensaje y evento que casi todo el mundo confunde, los tres servicios con su tabla decisoria, colas y temas con filtros y cola de mensajes con problemas de entrega, distribución de eventos con CloudEvents, ingesta masiva de telemetría con captura hacia el Data Lake, y el diseño completo del flujo de Contoso con las consecuencias que ya intuyes: entrega al menos una vez, ordenación e idempotencia.

Curso de Azure

Módulo 1: Introducción a Azure

Módulo 2: Servicios principales de Azure

Módulo 3: Bases de datos de Azure

Módulo 4: Seguridad en Azure

Módulo 5: Azure DevOps

Módulo 6: Servicios avanzados de Azure

Módulo 7: Monitoreo y gestión

Módulo 8: Gestión y optimización de costos

Módulo 9: Estudios de caso y mejores prácticas

© Copyright 2026. Todos los derechos reservados