Today, when a passenger clicks "Confirm booking" in app-contoso-reservas-pro, six things happen one after another inside that single HTTP request: the payment gateway is charged, availability is updated, the boarding pass issue is enqueued, the miles are credited, the billing system is notified and the confirmation email is sent. All synchronous, all inline. The consequence is brutal and Contoso Airlines has already lived through it: if the miles service is slow, the purchase fails. A peripheral component takes down the company's main revenue stream.
This lesson solves that problem at the root through decoupling: components stop calling each other and start exchanging messages and events through an intermediary infrastructure. You will see the three services Azure offers for this, when to use each, and the design consequences this model brings, which are not free.
Contents
- Why decouple
- Message versus event
- The three services compared
- Azure Service Bus
- Azure Event Grid
- Azure Event Hubs
- Contoso's complete flow
- At-least-once delivery, ordering and idempotency
- The cost of each service
- Common Mistakes and Tips
- Exercises
- Conclusion
- Why decouple
Chaining six synchronous calls has three cumulative flaws. Availability multiplies: if each component is at 99.9%, the chain of six drops to 99.4%, more than four hours of downtime a month. Latency adds up: the passenger waits for all of them. And coupling freezes things: adding a seventh step forces you to touch and redeploy the purchase code.
With an intermediary, the purchase does the bare essentials — take the payment and reserve the seat — and publishes a fact. The others react at their own pace. If the miles service is down, its message waits in the queue and is processed when it comes back; the passenger never notices. That is decoupling: in time, in space and in availability.
- Message versus event
This is the distinction almost everybody confuses, and choosing the right service depends on it.
| Message | Event | |
|---|---|---|
| What it expresses | An instruction: do this | A fact: this has happened |
| The sender's intent | Expects somebody to process it | Neither knows nor cares who reacts |
| Content | The full payload that is needed | Lightweight: what happened, when and about what |
| Coupling | The sender knows the recipient | None |
| If nobody consumes it | It is an error: it stays in the queue | It is normal: it is discarded |
| Example at Contoso | "Issue the boarding pass for locator XR7742" | "Booking XR7742 has been confirmed" |
The mnemonic: a message has a recipient and an intent; an event has a sender and a past tense. The verb form gives it away — EmitirTarjeta versus ReservaConfirmada — and that is not cosmetic: the message goes to Service Bus because somebody has to process it and it cannot be lost; the event goes to Event Grid because zero, one or seven subscribers may be interested and the sender is unaffected either way.
- The three services compared
| Service Bus | Event Grid | Event Hubs | |
|---|---|---|---|
| What for | Reliable enterprise messaging | Distributing discrete events | Massive ingestion of telemetry |
| Unit | Message (up to 256 KB / 100 MB) | Event (64 KB) | Record (small, in a stream) |
| Typical volume | Thousands per second | Millions per second | Millions per second, sustained |
| Delivery model | The consumer pulls | The service pushes | The consumer reads by offset |
| Ordering | Yes, with sessions | Not guaranteed | Yes, within the partition |
| Retries and failed messages | Complete | Yes, with retries and a queue | The consumer manages its position |
| Retention | Until consumed | 24 hours of retries | A time window (1-7 days or more) |
| Case at Contoso | reservas-confirmadas |
ReservaConfirmada, new blob |
Check-in desk telemetry |
And do not forget the Azure Storage queues from 02-04: simple, very cheap, with no topics, no sessions and no transactions, with 64 KB messages and seven days of retention. They are enough when there is a single consumer, order does not matter and you need neither filters nor transactional delivery. cola-emision-tarjetas is exactly that case, which is why it is still a Storage queue. The moment you need topics, sessions, duplicate detection or transactions, it is Service Bus.
- Azure Service Bus
az servicebus namespace create -g rg-contoso-reservas-pro -n sb-contoso-pro \
--location westeurope --sku Premium --capacity 1 \
--tags entorno=produccion proyecto=contoso-reservas \
centro-coste=CC-1042 [email protected]
# A TOPIC: one publisher, several interested parties with different criteria
az servicebus topic create -g rg-contoso-reservas-pro --namespace-name sb-contoso-pro \
-n reservas-confirmadas --max-size 5120 --enable-duplicate-detection true
az servicebus topic subscription create -g rg-contoso-reservas-pro \
--namespace-name sb-contoso-pro --topic-name reservas-confirmadas \
-n tarjetas --max-delivery-count 5 --dead-lettering-on-message-expiration true
# Filter: fidelizacion only wants bookings made by members
az servicebus topic subscription rule create -g rg-contoso-reservas-pro \
--namespace-name sb-contoso-pro --topic-name reservas-confirmadas \
--subscription-name fidelizacion -n solo-socios \
--filter-sql-expression "esSocio = true AND importe > 0"The pieces you need to know:
- Queue versus topic. A queue is point to point: each message is processed by one consumer. A topic is publish and subscribe: each subscription receives its own copy. Contoso uses a topic with the
tarjetas,facturacionandfidelizacionsubscriptions, so that adding a fourth interested party does not touch the sender. - Subscription filters. SQL rules over the message properties.
fidelizacionreceives only members' bookings, so it never sees what does not concern it: less processing and less cost. - Sessions. Without them there is no guaranteed ordering. With a
SessionId— the record locator, for example — all the messages in that session are processed by a single consumer and in order. That is what lets you guarantee that "booking created" is processed before "booking modified". - Peek-lock. The correct receive mode: the consumer locks the message without deleting it, does the work, and only then completes it. If the process dies, the lock expires and the message becomes available again. The alternative mode, receive-and-delete, loses messages on any failure.
- Deferral. Setting aside a message that cannot be processed yet — a piece of data is missing and will arrive later — without blocking the queue, so you can retrieve it later by its sequence number.
- Dead-letter queue. The piece that saves teams. After
max-delivery-countfailed attempts, the message is moved to a subqueue instead of blocking the process forever. There it is inspected, corrected and resubmitted. Without it, a poison message jams the queue indefinitely. - Transactions and duplicate detection. Several operations — completing one message and publishing another — can be grouped into an atomic unit within the same namespace. And with duplicate detection enabled, the service discards messages with the same
MessageIdfor a configured time window.
| Tier | When |
|---|---|
| Basic | Queues only. No topics and no sessions: it is hardly ever enough |
| Standard | Topics, sessions, transactions. Shared, variable capacity |
| Premium | Dedicated resources, predictable throughput, private endpoints. Production |
Sending and consuming, with a managed identity and no connection strings:
// Publish: the fact carries properties so that the filters can act on them
var client = new ServiceBusClient("sb-contoso-pro.servicebus.windows.net",
new DefaultAzureCredential());
var sender = client.CreateSender("reservas-confirmadas");
var message = new ServiceBusMessage(BinaryData.FromObjectAsJson(booking))
{
MessageId = booking.Localizador, // duplicate detection
SessionId = booking.Localizador, // ordering per booking
ContentType = "application/json"
};
message.ApplicationProperties["esSocio"] = booking.EsSocio; // used by the filter
message.ApplicationProperties["importe"] = booking.Importe;
await sender.SendMessageAsync(message);
// Consume with peek-lock: complete ONLY if the work finished successfully
var processor = client.CreateProcessor("reservas-confirmadas", "tarjetas",
new ServiceBusProcessorOptions { MaxConcurrentCalls = 10, AutoCompleteMessages = false });
processor.ProcessMessageAsync += async args =>
{
var booking = args.Message.Body.ToObjectFromJson<Booking>();
try
{
await _boardingPassIssuer.IssueAsync(booking); // idempotent (06-03)
await args.CompleteMessageAsync(args.Message);
}
catch (InvalidDataException ex)
{
// Permanent error: retrying makes no sense, straight to the subqueue
await args.DeadLetterMessageAsync(args.Message, "InvalidData", ex.Message);
}
// Any other exception: it is not completed, the lock expires and it is retried
};
await processor.StartProcessingAsync();Note the distinction in the catch: on a permanent error it goes to the dead-letter subqueue immediately, because retrying malformed data five times only wastes time; on a transient error you let the lock expire and the message is retried on its own.
- Azure Event Grid
Event Grid is an event router with push-based publish/subscribe: the service pushes the event to the destination and handles the retries. There is nothing to poll.
- System topics: Azure services themselves publish events with no configuration. A new blob in
sttarjetascontosoproemitsMicrosoft.Storage.BlobCreated, and Contoso uses it to trigger indexing of the boarding pass just generated. The same goes for Key Vault warning about a certificate close to expiry, or ACR announcing a new image. - Custom topics: you publish them yourself, and that is where
ReservaConfirmadagoes. - CloudEvents schema: the CNCF standard, and today's recommendation over Event Grid's own schema, because it makes events portable.
- Filters: by event type, by subject prefix or suffix and by advanced property values. Filtering happens in the service, so the subscriber receives only what interests it.
- Retries and dead-lettering: if the destination does not respond, it retries with exponential backoff for 24 hours and then deposits the event in a configured storage account. If you do not configure one, the event is silently lost, which is the most common trap in the service.
az eventgrid topic create -g rg-contoso-reservas-pro -n evgt-contoso-reservas-pro \
--location westeurope --input-schema cloudeventschemav1_0
az eventgrid event-subscription create --name sub-motor-disponibilidad \
--source-resource-id $(az eventgrid topic show -g rg-contoso-reservas-pro \
-n evgt-contoso-reservas-pro --query id -o tsv) \
--endpoint-type azurefunction \
--endpoint "<function id>" \
--included-event-types Contoso.Reservas.ReservaConfirmada \
--advanced-filter data.importe NumberGreaterThan 500 \
--deadletter-endpoint "<dead-letter container id>"The event in CloudEvents form, showing the essentials: it is lightweight and describes a fact, it does not carry the whole booking.
{
"specversion": "1.0",
"type": "Contoso.Reservas.ReservaConfirmada",
"source": "/contoso/reservas/app-contoso-reservas-pro",
"id": "b41f7c22-9e0a-4a11-9a3c-7d2f5c1e8a90",
"subject": "reservas/XR7742",
"time": "2026-08-15T09:14:22Z",
"datacontenttype": "application/json",
"data": { "localizador": "XR7742", "vuelo": "CA-1187",
"origenDestino": "BCN-CDG", "importe": 214.50, "esSocio": true }
}type lets you filter by class of event, subject by a specific resource — with prefix filters such as reservas/XR — and id is the identifier the consumer uses to detect duplicates.
- Azure Event Hubs
Event Hubs is not for business events but for large-scale data streams: telemetry, logs, time series. Contoso uses it for the check-in desks, which continuously emit the state of the passenger queue, the handling time and baggage incidents.
- Partitions: the stream is split into partitions that are read in parallel. Ordering is guaranteed within a partition, so the partition key — the desk identifier — determines what ends up ordered. The number of partitions fixes the maximum parallelism and is worth thinking through when you create the resource.
- Consumer groups: each consuming application has its own view of the stream with its own progress. The real-time dashboard and the analytics process read the same data without getting in each other's way.
- Retention: records are not deleted when they are read; they stay for a time window, 1 to 7 days on Standard and longer on Premium. That allows reprocessing by moving the offset back, which is impossible with a queue.
- Capture: it automatically writes the stream in Avro onto
stlagocontosopro, in the Data Lake'sbroncelayer (03-06), without writing a line of code. This is the key integration with analytics. - Kafka compatibility: a Kafka producer or consumer points at Event Hubs by changing the connection configuration, without touching the code. It is the migration route that does not involve operating a cluster.
az eventhubs namespace create -g rg-contoso-reservas-pro -n evhns-contoso-telemetria-pro \
--location westeurope --sku Standard --capacity 2 --enable-auto-inflate --maximum-throughput-units 10
az eventhubs eventhub create -g rg-contoso-reservas-pro \
--namespace-name evhns-contoso-telemetria-pro -n facturacion-mostradores \
--partition-count 8 --retention-time-in-hours 72 \
--enable-capture true --capture-interval 300 \
--destination-name EventHubArchive.AzureBlockBlob \
--storage-account stlagocontosopro --blob-container bronce--enable-auto-inflate raises the throughput units automatically on a spike — and here comes the cost warning: they go up on their own, but they do not come down on their own, so a one-off spike leaves the resource billing at maximum until somebody adjusts it.
- Contoso's complete flow
graph LR WEB["app-contoso-reservas-pro<br/>Confirm booking"] -->|1 message| SB["Service Bus<br/>reservas-confirmadas topic"] WEB -->|2 event| EG["Event Grid<br/>evgt-contoso-reservas-pro"] SB --> S1["tarjetas subscription"] --> F1["Function<br/>GenerarTarjetaEmbarque"] SB --> S2["facturacion subscription"] --> CA["ca-motor-facturacion"] SB --> S3["fidelizacion subscription<br/>filter esSocio = true"] --> F2["Function<br/>AcreditarMillas"] F1 --> BLOB["sttarjetascontosopro<br/>tarjetas-embarque"] BLOB -->|BlobCreated| EG2["Event Grid<br/>system topic"] --> LA["Logic App:<br/>notify the passenger"] EG --> ML["Demand model"] DESK["Check-in desks"] -->|telemetry| EH["Event Hubs<br/>facturacion-mostradores"] EH -->|capture| LAKE["stlagocontosopro / bronce"] EH --> PANEL["Real-time<br/>operations dashboard"] SB -.->|failures| DLQ["Dead-letter<br/>queue"]
Read it as a design decision on every arrow. The confirmation publishes a message to the topic, because issuing the boarding pass, billing and crediting miles have to happen and cannot be lost; and it publishes an event to Event Grid, because there may be interested parties that do not exist today — the demand prediction model is one — without the sender knowing about them. When the function drops the PDF into the container, Storage's system topic emits BlobCreated and a logic app (06-04) notifies the passenger: nobody had to program that connection. Check-in desk telemetry goes through Event Hubs, with automatic capture to the Data Lake and simultaneous reading by the real-time dashboard from another consumer group. And the passenger's request finishes as soon as the payment is taken and the seat is reserved: if loyalty is down, its message waits in the subscription and the purchase never notices. That was the problem at the start of the lesson, and it is solved.
- At-least-once delivery, ordering and idempotency
These are the three unavoidable consequences of the model, and it is better to accept them as part of the design rather than meet them as surprises.
At-least-once delivery. All three services guarantee that the message arrives, not that it arrives only once. A consumer can receive a duplicate if it crashes after processing and before completing, if the lock expires because it took too long, or if the sender retries after not receiving the acknowledgement. End-to-end exactly-once delivery does not exist in a distributed system; what does exist is idempotent processing, which makes it unnecessary.
Ordering. By default there is no global ordering. You get ordering per session in Service Bus and per partition in Event Hubs, and Event Grid does not guarantee it at all. Design so that order does not matter wherever you can — including a timestamp or a version number in the payload and discarding stale data — and reserve sessions for the cases where it genuinely matters, because they limit parallelism.
Idempotency, already seen in 06-03 and now mandatory in every consumer. The three tools: MessageId with duplicate detection in Service Bus, which covers the short window; the recorded event identifier on the consumer side, with the registroembarques table in stoperacionescontosopro; and naturally idempotent operations, writing with a deterministic key or upsert instead of insert. That last one is always the best because it needs no additional infrastructure.
- The cost of each service
| Service | How it is billed | What drives the bill up |
|---|---|---|
| Storage queues | Per transaction and storage | Almost nothing; it is the cheapest option |
| Service Bus Standard | Per operation, with a monthly base | Aggressive polling: every empty receive attempt is an operation |
| Service Bus Premium | Per messaging unit per hour | Over-sizing "just in case" |
| Event Grid | Per operation (million operations) | Unfiltered subscriptions that receive everything |
| Event Hubs | Per throughput unit per hour + events | Auto-scaling that goes up and never comes down, and Capture |
Three concrete tips: use long-polling receives in Service Bus instead of polling in a loop, because one empty receive per second is 2.6 million operations a month per consumer; filter in the service, not in the consumer, because what is filtered out in Event Grid is not billed as a delivery; and review the Event Hubs throughput units after every spike, because auto-inflate goes up but never comes down. In development, delete namespaces that are not in use: a Service Bus Premium or an Event Hubs with reserved units bills whether it is empty or full.
Common Mistakes and Tips
- Using Event Hubs for business messaging. It has neither per-message locking nor a dead-letter queue: failure handling is yours and you will end up reimplementing Service Bus badly.
- Not configuring the dead-letter queue in Service Bus or the dead-letter destination in Event Grid. Failures become invisible and data is lost.
- Putting the full payload in the event. An event describes a fact and carries a reference; if you need to send 5 MB, upload it to
sttarjetascontosoproand send its URI. - Assuming ordering where there is none. If order is essential, sessions or partitions; if not, a version stamp in the payload.
- Not making the consumer idempotent. Sporadic duplicates, impossible to reproduce and always in production.
- Retrying a permanent error indefinitely. Distinguish transient from permanent and send the latter to the subqueue immediately.
- Polling in a tight loop. It sends the operations bill through the roof. Use long-polling receives.
- Tip: give events past-tense fact names (
ReservaConfirmada) and messages instruction names (EmitirTarjeta). The name forces you to decide which is which, and once you have, the service chooses itself. - Tip: version the schema from the very first event, with
Contoso.Reservas.ReservaConfirmada.v1. The day the payload changes, old consumers will keep working.
Exercises
Exercise 1. Contoso Miles wants that, when a flight is completed, the miles are credited to the member, their tier is recalculated and a monthly summary is sent; on top of that, the data team wants every flight-completed event for its prediction model, and the boarding gates emit 400 readings per second that have to be archived and visualized live.
- Assign each need to a service and justify the choice using the message/event distinction.
- Design the topic, the subscriptions and their filters for the Service Bus part.
- State the mandatory tags and one cost-saving measure for the development environment.
Exercise 2. The facturacion subscription has accumulated 12,000 messages in its dead-letter queue. On inspection, they are all from the same day and have DeadLetterReason: MaxDeliveryCountExceeded.
- What happened, and why did the main queue keep working?
- Describe the procedure for recovering them, stating what has to be verified before resubmitting.
- Propose two measures so that it does not happen again, and one alert.
Exercise 3. A consumer of reservas-confirmadas takes around 8 minutes to process each message because it calls a slow external system. The Service Bus lock is 5 minutes. Explain what is going on, what you see in production, and give two solutions of a different nature.
Solutions
Solution 1:
- Crediting miles and recalculating the tier are messages: they are instructions that have to be executed and cannot be lost, so Service Bus. The notification to the data team is an event — "flight completed", a fact anyone can subscribe to, and tomorrow perhaps others will — so Event Grid. The 400 readings per second from the gates are high-volume telemetry that has to be archived and watched live: Event Hubs with capture to the Data Lake and a second consumer group for the dashboard.
- A
vuelos-completadostopic insb-contoso-pro, with themillassubscription unfiltered and thenivel-fidelidadsubscription with the filteresSocio = true AND millasAcreditadas > 0. Both withmax-delivery-count 5and dead-lettering enabled. The monthly summary is not a subscription: it is a scheduled process, and putting it here would confuse messaging with scheduling. entorno,proyecto=contoso-millas,centro-coste=CC-2077andpropietario. Savings in development: the Standard tier instead of Premium for Service Bus, a single throughput unit with no auto-inflate in Event Hubs with 24-hour retention, and deleting namespaces when they are not in use, because they bill even when empty.
Solution 2:
- The billing consumer failed systematically throughout that day — a deployment with a bug, or the billing system down. Each message was retried 5 times and, once
max-delivery-countwas exceeded, Service Bus automatically moved it to the subqueue. That is precisely why the main queue kept working: without that mechanism, the first poison message would have blocked the process and the other subscriptions would have been affected too. The subqueue did exactly its job. - Before resubmitting anything, three things have to be verified: that the root cause is fixed and deployed, that the consumer is idempotent — because some of those messages may have been partially processed — and what
DeadLetterReasonandDeadLetterErrorDescriptionsay for a representative sample, in case more than one cause is mixed in. Then the messages are read from the subqueue and resubmitted to the topic in controlled batches, watching the main queue so as not to repeat the jam. - Measures: (a) distinguish permanent errors from transient ones in the code and send the former to the subqueue immediately, without burning through five attempts; (b) a circuit breaker that pauses the consumer when the failure rate exceeds a threshold, so that messages wait in the queue instead of burning their retries against a system that is down. The alert: on the subqueue length, with a low threshold — ten messages — and a page to the on-call team, because 12,000 messages means nobody was looking.
Solution 3: The lock expires after 5 minutes while the consumer is still working; Service Bus treats the message as unprocessed and delivers it to another consumer. When the first one finishes and calls CompleteMessageAsync, it fails with a lock-lost error. In production this shows up as messages processed two and three times, a delivery count that climbs until it runs out, and messages that end up in the subqueue despite having been processed correctly. Solution (a), tactical: renew the lock automatically while the work is in progress (MaxAutoLockRenewalDuration) and increase the subscription's lock duration. Solution (b), architectural and better: take the slow call out of the consumer; complete the message straight away and delegate the slow part to a separate flow — a Durable Functions orchestration or a second queue — because holding a message locked for eight minutes limits the throughput of the entire system.
Conclusion
You have solved the problem the lesson opened with. You know why you decouple: availability multiplies downwards, latency adds up and coupling freezes evolution; with an intermediary, the purchase does the bare essentials and publishes, and a peripheral component going down no longer takes the main revenue stream with it. You are clear about the distinction almost everybody confuses: a message is an instruction with a recipient that cannot be lost; an event is a past-tense fact that the sender publishes without knowing who reacts. And you know the name gives it away.
You handle the three services with judgment. Service Bus for reliable enterprise messaging: queues versus topics with subscriptions and SQL filters, sessions for ordering, peek-lock as the correct receive mode, deferral, the dead-letter queue — the piece that saves teams — transactions, duplicate detection and its three tiers, with the send and consume code for reservas-confirmadas distinguishing permanent errors from transient ones. Event Grid for distributing discrete events: system topics that emit with nothing configured — the BlobCreated from sttarjetascontosopro — custom topics, the CloudEvents schema, filtering in the service, and the dead-letter destination you must configure or events are silently lost. And Event Hubs for massive ingestion: partitions and ordering within the partition, independent consumer groups, retention that allows reprocessing, automatic capture into the bronce layer of stlagocontosopro and Kafka compatibility. Without forgetting that the humble Storage queues are enough when there is a single consumer and no demanding requirements.
You have designed Contoso's complete flow seeing every decision, and you take away the three unavoidable consequences: at-least-once delivery — exactly-once delivery does not exist, what exists is idempotent processing — ordering only per session or per partition, and mandatory idempotency in every consumer. Plus the cost of each service and what drives it up: polling in a loop, unfiltered subscriptions and auto-scaling that goes up and never comes down.
With this, Contoso's platform is modernized: containers, orchestration, functions, integrations and an event architecture holding it all up. One last step of the module remains, and it is of a different nature. Contoso now has data it could not exploit before — thousands of passenger complaints written in free text, passports that an agent types in by hand at check-in, boarding announcements available in only two languages, a portal that makes no sense outside Spain — and there are ready-made services for all of it. The next lesson, Azure AI Services, walks through that catalog, implements Contoso's concrete cases, explains Azure OpenAI Service and retrieval-augmented generation for the passenger support assistant, and gives proper space to what is not optional: bias, hallucinations, the privacy of the data sent to the service and prior review by the legal and compliance teams.
Azure Course
Module 1: Introduction to Azure
- What Is Azure?
- Service Models, Regions and Availability Zones
- Creating and Setting Up Your Azure Account
- A Tour of the Azure Portal
- Azure Resource Manager: Subscriptions, Resource Groups and Tags
- Azure CLI, PowerShell and Cloud Shell
Module 2: Core Azure Services
- Azure Virtual Machines
- Compute Scaling and High Availability
- Azure App Service
- Azure Storage: Blobs, Files, Queues and Tables
- Azure Networking: Virtual Networks, Subnets and NSGs
- Hybrid Connectivity and Global Delivery
Module 3: Azure Databases
- Choosing the Right Data Service
- Azure SQL Database
- Azure Cosmos DB
- Azure Database for MySQL
- Azure Database for PostgreSQL
- Data Analytics: Data Lake, Data Factory and Synapse
Module 4: Security in Azure
- Microsoft Entra ID and Identity Management
- RBAC and Managed Identities
- Azure Key Vault
- DDoS Protection and Web Application Firewall
- Microsoft Defender for Cloud
- Governance and Compliance with Azure Policy
Module 5: Azure DevOps
- Introduction to Azure DevOps
- Azure Repos
- Azure Pipelines: Continuous Integration
- Continuous Deployment with Environments and Approvals
- Azure Artifacts
- Infrastructure as Code with Bicep
Module 6: Advanced Azure Services
- Containers in Azure: Container Registry and Container Apps
- Azure Kubernetes Service (AKS)
- Azure Functions
- Azure Logic Apps
- Messaging and Events: Service Bus, Event Grid and Event Hubs
- Azure AI Services
Module 7: Monitoring and Management
- Azure Monitor: Metrics, Alerts and Dashboards
- Log Analytics and KQL Queries
- Application Insights
- Azure Automation and Runbooks
- Backup and Disaster Recovery
Module 8: Cost Management and Optimization
- Pricing Calculator and Cost Estimation
- Azure Cost Management: Analysis, Budgets and Alerts
- Reservations, Savings Plans and Azure Hybrid Benefit
- Azure Advisor
- Optimization Strategies and FinOps Culture
