The contoso-reservas project already exists, but it is empty. The bookings website code is still where it was: on Diego Salas' laptop, in a folder under local version control that nobody else can see, and in a succession of ZIPs in Marta's inbox. This lesson eliminates that situation at the root.
Azure Repos is Azure DevOps' private Git repository service. Hosting code in a remote repository is the easy part — anybody can do that. What really transforms the way a team works is what you build on top: a branching strategy everybody understands, pull requests that turn review into a recorded conversation, and branch policies that stop the team's rules from depending on somebody remembering them. By the end, Contoso's main branch will be, by construction, always deployable.
Contents
- The Git essentials: commit, branch, merge and rebase
- Creating the
contoso-reservasrepository and cloning it - Authentication: Credential Manager, tokens and SSH
- Branching strategies and the one Contoso chooses
- Pull requests
- Branch policies on
main - Automatic reviewers by path: Azure Repos' CODEOWNERS
- Tags and semantic versioning
.gitignoreand the mistake of pushing a secret- Migrating the local repository while preserving history
- Git LFS for heavy graphic assets
- Comparison with GitHub Repos
- Common Mistakes and Tips
- Exercises
- Conclusion
- The Git essentials: commit, branch, merge and rebase
This is not a Git lesson, but there are four concepts without which the rest makes no sense. A commit is a complete snapshot of the project, identified by a cryptographic hash and pointing at the previous commit: a repository's history is a chain of snapshots, not a list of diffs. A branch is just a movable pointer to a commit — creating one means writing a 41-byte file — which is why modern workflows create and destroy branches constantly.
Merge and rebase are two ways of bringing one branch's work into another, and the difference matters:
| Merge | Rebase | |
|---|---|---|
| What it does | Creates a new commit with two parents | Rewrites the commits onto another base |
| Resulting history | Branched, reflecting what actually happened | Linear, easier to read |
| Risk | None | Never on already-shared branches |
| Typical use at Contoso | Never by hand: the pull request does it | Updating your branch with main before asking for review |
git switch -c feature/1842-cabin-map # Create a branch from main and switch to it
git commit -am "Add the cabin map with seat selection. AB#1842"
git fetch origin && git rebase origin/main # Rewrite my commits on top of main
git add . && git rebase --continue # After resolving a conflict: mark it and carry on
git push -u origin feature/1842-cabin-map # Publish the branch in Azure ReposThe golden rule of rebase: only on your personal branch and before anyone else works on it. Rebasing main rewrites everybody's history and is the fastest way to ruin your team's day.
- Creating the
contoso-reservas repository and cloning it
contoso-reservas repository and cloning itContoso creates four repositories in the project, following the rule "one deployable artifact, one repository":
| Repository | Contents | Deployed to |
|---|---|---|
contoso-reservas |
The public website | app-contoso-reservas-pro/dev |
contoso-api-disponibilidad |
The Availability API | app-contoso-api-disponibilidad-pro/dev |
contoso-modelos |
Shared library of booking models | A package in the feed (05-05) |
contoso-infra |
Bicep templates for the whole platform | Azure resources (05-06) |
# Create the four repositories with the azure-devops CLI extension
for r in contoso-reservas contoso-api-disponibilidad contoso-modelos contoso-infra; do
az repos create --name "$r"
done
az repos list --query "[].{name:name, url:webUrl}" -o table
git clone https://[email protected]/contoso-airlines/contoso-reservas/_git/contoso-reservas
- Authentication: Credential Manager, tokens and SSH
There are three ways for your local Git to authenticate against Azure Repos, and they are not equivalent:
| Method | How it works | Recommendation |
|---|---|---|
| Git Credential Manager | Opens the browser, authenticates with Entra ID (and MFA) and stores a short-lived token in the system credential store | The default option on people's laptops |
| Personal access token (PAT) | A manually generated string, with scopes and an expiry date, that acts as a password | Only for automation that accepts nothing else |
| SSH keys | A key pair; the public one is registered in your profile | Good for Linux teams or anyone who prefers not to depend on a browser |
Git Credential Manager ships with recent Git installations and respects the conditional access and MFA from 04-01: the session on Diego's laptop expires when the Entra ID policy says so, not when somebody happens to think of it. Personal access tokens are the most common source of security incidents in Azure DevOps. If you have to use one: grant it only the scopes it needs (Code (read) for a tool that only reads) and never Full access; give it the shortest viable expiry; store it in kv-contoso-pro and not in a configuration file; and bear in mind that a PAT inherits all the permissions of whoever created it, so an administrator's PAT is a master key.
ssh-keygen -t ed25519 -C "[email protected]" # Generate the key pair
ssh-add ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub # The public key is pasted into your Azure DevOps profile
# From then on you clone over SSH, with no passwords and no tokens
git clone [email protected]:v3/contoso-airlines/contoso-reservas/contoso-reservasFor pipelines, none of these three: they will use the workload identity federation of the service connections from 05-01. The consistency is the same as in module 4: if there is a way to authenticate without a secret, that is the one you use.
- Branching strategies and the one Contoso chooses
| Strategy | How it works | Advantages | Drawbacks | Fits |
|---|---|---|---|---|
| GitFlow | develop, release/*, hotfix/*, feature/* and main branches |
Supports several live versions at once | Complex, long merges, slows delivery down | Installable software with maintained versions |
| GitHub Flow | main + feature branches; deploy on merge |
Simple, ideal for continuous deployment | Requires solid automated tests | Web services with a single live version |
| Trunk-based development with feature branches | main always deployable + very short-lived branches (1-2 days) |
Trivial merges, fast feedback, better DORA metrics | Demands discipline and feature flags | Teams deploying daily |
| Pure trunk-based | Everyone commits straight to main |
Maximum integration speed | No prior review; unworkable with PCI DSS auditing | Very mature teams with exhaustive tests |
Contoso chooses trunk-based development with feature branches for three specific reasons: there is only one live version — the one on the internet — so GitFlow would solve a problem Contoso does not have while charging its complexity anyway; the PCI DSS audit requires review of changes, which rules out pure trunk-based with no pull requests; and short branches attack its worst DORA metric head on, the lead time for changes. The agreed naming convention: feature/<id>-<description>, bugfix/<id>-<description> and hotfix/<id>-<description>, always with the work item identifier up front.
gitGraph
commit id: "v2.4.0"
branch feature/1842-cabin-map
commit id: "map API"
checkout main
branch bugfix/1855-seats
commit id: "fix seat count"
checkout main
merge bugfix/1855-seats id: "PR 58"
checkout feature/1842-cabin-map
commit id: "rebase onto main"
checkout main
merge feature/1842-cabin-map id: "PR 61" tag: "v2.5.0"
The rule that makes this work: a branch open for more than two days is a warning sign. If the work is bigger than that, it is split into increments that can be merged into main without being switched on yet, using the feature flags from 05-04.
- Pull requests
A pull request proposes merging one branch into another and opens a conversation about it. It is the central quality mechanism and, incidentally, the documentary record the auditor was asking for in 05-01.
# Create the pull request from the CLI, linking the work item
az repos pr create --repository contoso-reservas \
--source-branch feature/1842-cabin-map --target-branch main \
--title "Cabin map with seat selection" \
--description "Implements AB#1842. Includes tests for the seat count." \
--work-items 1842 --reviewers [email protected] \
--delete-source-branch true --squash true--delete-source-branch true deletes the branch on completion: without it, in six months there will be two hundred dead branches and nobody will know which ones are still alive. --squash true is explained in the next section.
Contoso defines a description template in .azuredevops/pull_request_template.md, which Azure Repos preloads into every new pull request:
## What changes and why
<!-- Context for the reviewer: the problem, not just the solution -->
## Work item
AB#
## How it was tested
- [ ] New or updated unit tests
- [ ] Tested on `app-contoso-reservas-dev`
## Risks and rollback
<!-- What can break and how to go back -->
## Checklist
- [ ] No secrets or connection strings in the code
- [ ] Backward-compatible database migrationsFour practices separate a useful review from a formality. Small pull requests: above 400 lines, review quality falls off a cliff and things get approved out of fatigue, whereas a 60-line one gets real comments. Suggested changes: Azure Repos lets you propose the exact text from within a comment, and the recipient applies it with one click. Comment on the code, not the person: "this query makes one call per seat" instead of "you have written an N+1". And above all, the conversation is documentation: when Marta asks "why 90 seconds of cache and not 300?" and Diego answers with the figure from the fares provider, that answer stays attached to the change forever. It is the best architecture documentation that exists, because nobody has to remember to write it separately.
- Branch policies on
main
mainThis is where the verbal agreement becomes an enforced rule. A branch policy is a condition that must be met before a pull request can be completed. Contoso protects main with six:
| Policy | Contoso's configuration | What it prevents |
|---|---|---|
| Minimum number of reviewers | 2, and the author cannot approve | A change getting in with no outside eyes |
| Check for linked work items | Required | Changes with no traceable justification |
| Check for comment resolution | All resolved | Objections quietly ignored |
| Build validation | CI pipeline (05-03), required | Merging code that does not build or fails tests |
| Limit merge types | Squash merge only | An unreadable main history |
| Automatic reviewers | By path (section 7) | Nobody with the necessary context finding out |
COMMON="--repository-id $REPO_ID --branch main --blocking true --enabled true"
# 1. Minimum of 2 reviewers, no self-approval, resetting the
# approvals if new commits arrive after approval
az repos policy approver-count create $COMMON \
--minimum-approver-count 2 --creator-vote-counts false \
--reset-on-source-push true --allow-downvotes false
# 2. Linked work item required (traceability)
az repos policy work-item-linking create $COMMON
# 3. All comments resolved before completing
az repos policy comment-required create $COMMON
# 4. Build validation: the CI pipeline must pass,
# and its result expires after 12 hours (720 minutes)
az repos policy build create $COMMON \
--build-definition-id $CI_PIPELINE_ID \
--display-name "Contoso Bookings CI" \
--valid-duration 720 --queue-on-source-update-only true
# 5. Squash merge is the only type allowed
az repos policy merge-strategy create $COMMON --allow-squash true \
--allow-no-fast-forward false --allow-rebase false --allow-rebase-merge falseThree details people overlook. --reset-on-source-push true: if new commits arrive after approval, the approvals are voided — without this, someone can approve a two-line change and then slip five hundred lines in behind it. --valid-duration 720: the build expires after 12 hours, which stops you merging something validated against a main that has since become something else. And squash merge, which makes each pull request appear in main as a single clean commit: the history becomes the list of functional changes delivered, exactly what somebody investigating an incident wants to read.
The policies apply to administrators too, and that is the whole point. There is an option to grant "bypass policies", and the right answer is almost always not to grant it: if you need to skip the process because of an emergency, the process is badly calibrated. For genuine emergencies, Contoso keeps hotfix/* branches with their own policy of a single reviewer, going through the same pipeline.
- Automatic reviewers by path: Azure Repos' CODEOWNERS
In GitHub, a CODEOWNERS file assigns owners per folder. Azure Repos does the same with the automatically included reviewers policy, configured per path — not in a file in the repository, but in the policy itself:
# Infrastructure and pipelines: mandatory review by Contoso-Infraestructura
az repos policy required-reviewer create $COMMON \
--message "Infrastructure changes: review by Contoso-Infraestructura" \
--required-reviewer-ids "Contoso-Infraestructura" \
--path-filter "/infra/*;/*.bicep;/azure-pipelines*.yml"
# Same for the data schema, with Contoso-DBA-Reservas and the filter
# --path-filter "/src/Contoso.Reservas.Datos/Migraciones/*"The groups are the same ones from module 4, so people management stays in a single place. The practical effect: Diego can no longer change the db-reservas schema without a DBA finding out, and not because he has been forbidden to, but because the merge will not complete.
- Tags and semantic versioning
A tag marks a specific commit with a permanent name. It is what lets you answer "which exact code is version 2.5.0, the one in production?".
Contoso uses semantic versioning MAJOR.MINOR.PATCH:
| Part | When it goes up | Example at Contoso |
|---|---|---|
| MAJOR | Breaking change | The Availability API stops accepting the old date format |
| MINOR | New backward-compatible functionality | The cabin map is added |
| PATCH | Backward-compatible fix | The seat count is fixed |
# Annotated tag (with author, date and message) on the current commit
git tag -a v2.5.0 -m "Cabin map and seat count fix"
git push origin v2.5.0 # Tags do NOT travel with a normal git push
git show v2.5.0 --stat # Exactly which commit the deployed version isIn 05-05 the package version number will be generated automatically from the pipeline, and in 05-04 the tag will be created by itself on deployment to production, so that the correspondence between tag and environment does not depend on anybody typing it by hand.
.gitignore and the mistake of pushing a secret
.gitignore and the mistake of pushing a secretThe .gitignore file states what must never enter the repository:
bin/
obj/
node_modules/
appsettings.Development.local.json # Local configuration with secrets
.env
*.pfx # Private keys: never in the repository
*.pem
.vs/Now the serious mistake. Diego accidentally commits an appsettings.json with the connection string for sql-contoso-reservas-dev and the payment gateway key. He notices ten minutes later and deletes the file in the next commit. The secret is still compromised, because Git history is immutable and every commit keeps the full content: anybody who has cloned the repository — or any backup, or any build log — has the value. Deleting it afterwards does not remove it, it only hides it from a surface glance.
The correct procedure, in strict order:
- Rotate the secret immediately. That is the only thing that genuinely closes the hole: generate a new payment gateway key, update it in
kv-contoso-proand leave the previous one worthless. Everything else is cleanup, not remedy. - Check for usage. Review the resource's access logs in case the credential was used from outside; this is what the Microsoft Defender for Cloud alerts from 04-05 are for.
- Clean the history with
git filter-repo, coordinating a re-clone with the whole team. It is destructive and disruptive, and on large repositories it often is not worth it. - Prevent a repeat: a correct
.gitignore, review in the pull request and automatic detection.
Azure DevOps includes secret scanning that analyzes commits and warns you — and with GitHub Advanced Security for Azure DevOps, which is paid, it can block the push before the secret gets in. A local hook or an analysis step in the pipeline stops most of the remaining cases.
The underlying solution is one you already know from module 4: if there are no secrets, they cannot leak. Contoso's application authenticates against SQL and against storage with a managed identity, and the little that genuinely remains secret — pasarela-pago-clave, token-meteo — lives in kv-contoso-pro and is referenced with @Microsoft.KeyVault(...). The configuration file in the repository contains resource names, never credentials.
- Migrating the local repository while preserving history
Diego's repository holds three years of history that cannot be lost: it is what lets you know why each line was written. The migration preserves all of it:
cd ~/projects/contoso-reservas # Diego's existing local repository
git remote add azure https://[email protected]/contoso-airlines/contoso-reservas/_git/contoso-reservas
git push azure --all # ALL branches, with their history intact
git push azure --tags # ALL tags
git log azure/main --oneline | wc -l # Check that the history arrived complete
# If the origin was another Git server, the clean way is a mirror clone,
# which includes every ref and not just the local branches:
git clone --mirror https://servidor-antiguo.contosoairlines.example/reservas.git
cd reservas.git && git push --mirror $AZURE_REPOS_URLBefore calling the migration done, two things: set main as the default branch and make the old repository read-only. If it carries on accepting pushes, two divergent histories will appear and somebody will lose work.
- Git LFS for heavy graphic assets
The website includes destination images, high-resolution cabin maps and header videos: binaries tens of megabytes in size. Git stores every full version of every binary forever, so a 30 MB map modified twenty times is 600 MB permanently that everybody downloads when they clone. Git LFS (Large File Storage) replaces the binary in history with a text pointer and stores the content separately, downloading only the version you need:
git lfs install # Once per repository and machine
git lfs track "*.psd" "*.mp4" "recursos/imagenes/**/*.png"
git add .gitattributes # The tracking config is stored there
git commit -m "Configure Git LFS for heavy graphic assets"Two warnings: LFS has to be configured before you push the binaries (doing it afterwards requires rewriting history), and its storage counts against the organization's billable Artifacts quota. The assets the website serves in production live in a storage account behind the CDN from 02-06; only what is part of the source code goes into the repository.
- Comparison with GitHub Repos
| Azure Repos | GitHub Repos | |
|---|---|---|
| Branch protection | More granular policies, with path filters and expiry | Branch rules, simpler |
| Code owners | Reviewer policy by path | Versioned CODEOWNERS file |
| Code review | Good; suggested changes | Excellent; the industry standard |
| Code security | Secret scanning; paid Advanced Security | More mature Advanced Security; Dependabot |
| Ecosystem and community | Internal | Enormous |
In practice, GitHub's review experience tends to feel nicer, while Azure Repos' policies are more granular. Contoso stays on Azure Repos for the Boards integration and the traceability PCI DSS demands.
Common Mistakes and Tips
- Long-lived branches. A three-week branch guarantees a painful merge. If the work is big, split it into mergeable increments and hide it behind feature flags.
- Rebasing a shared branch. It rewrites everybody's history. Rebase only on your own branch and before anyone else works on it.
- Enormous pull requests and unread approvals. Beyond 400 lines the review is a formality; and the two-reviewer policy is worth nothing if the second signature is automatic. Better five 80-line pull requests and one honest review.
- Believing that deleting the commit deletes the secret. It does not. Always rotate, then clean up.
- Granting "bypass policies" to administrators. It turns the protection into a suggestion. If there are emergencies, configure a separate, more relaxed policy on
hotfix/*. - Pushing heavy binaries without LFS. The repository grows forever and cloning becomes unbearable; and remember that LFS consumes billable quota.
- Tip: write commit messages with a short imperative title, a body explaining why — the what is already visible in the diff — and the
AB#<id>reference. - Tip: enable automatic deletion of the source branch on pull request completion and periodically purge branches with no activity.
Exercises
Exercise 1: choosing and justifying the branching strategy
The Contoso Miles team (centro-coste=CC-2077) maintains a web portal and, on top of that, a mobile app whose version 3.2 is still installed on customers' phones and receives fixes while 4.0 is being developed.
- Which branching strategy suits each of the two products, and why are they different?
- For the portal, define the branch policies you would put on
mainalong with your reasoning. - How would you handle an urgent fix to the portal on a Saturday without skipping the process?
Exercise 2: a secret in the repository
Diego commits and pushes to main an appsettings.json containing the payment gateway key and the connection string for sql-contoso-reservas-pro. He realizes twenty minutes later. Two colleagues have already run git pull.
- List the actions in order, stating which one actually closes the risk.
- Why is a commit that deletes the file not enough, nor a revert?
- Which three technical measures would stop it happening again, and which module 4 architecture decision makes it almost impossible?
Exercise 3: policies and reviewers by path
Contoso wants: (a) nobody to merge into main without two approvals and without a work item; (b) every change under /src/Contoso.Reservas.Datos/Migraciones/ to be reviewed by Contoso-DBA-Reservas; (c) the main history to stay linear; (d) a build approved three days ago to be useless for merging today.
- State which specific policy solves each point and write the CLI command for (b).
- What happens if Marta, who is a project administrator, tries to merge without meeting them?
Solutions
Solution 1:
- For the portal, trunk-based development with feature branches: a single live version (the deployed one), frequent deployment and branches lasting a day or two. For the mobile app, a GitFlow-style flow or at least long-lived
release/3.2andrelease/4.0branches, because there are two live versions simultaneously: the one installed on phones, which is still getting patches, and the one under development. The difference is not a matter of taste: on the web you control which version the customer runs; on mobile you do not. - A minimum of 2 reviewers with no self-approval and reset on push; linked work item (traceability); comments resolved; build validation with a 12-hour expiry; squash merge only; and automatic reviewers by path for infrastructure and schema.
- A
hotfix/<id>-<description>branch frommain, with its own policy on thehotfix/*pattern requiring a single reviewer but keeping build validation and the work item. It goes through the same pipeline and the same deployment; the only thing relaxed is the number of eyes, not the automation. Never grant "bypass policies".
Solution 2:
- (a) Rotate the payment gateway key and change the database credential — this and only this closes the risk; update the new value in
kv-contoso-pro. (b) Review the access logs and the Defender for Cloud alerts in case there was misuse. (c) Add the file to.gitignoreand remove it from tracking. (d) Consider rewriting the history withgit filter-repoand coordinating the re-clone, given that the repository is private and the team is small. (e) Report it to the security lead: this is an incident, not an oversight. - Because Git history is immutable and every commit stores the full content: the value is still recoverable in any clone, backup or build log. A revert adds a new commit, it does not remove the previous one. As long as the credential is still valid, it is still compromised.
- Secret scanning enabled on the repository; a pre-commit hook or an analysis step in the CI pipeline; and a complete
.gitignorewith mandatory review of configuration files in the pull request. The architecture decision: authentication with a managed identity — no password-bearing connection string to leak — and the little that is irreducible in Key Vault, referenced with@Microsoft.KeyVault(...). If there is no secret in the code, there is no secret to push.
Solution 3:
- (a)
approver-countwith--minimum-approver-count 2 --creator-vote-counts false, pluswork-item-linking. (b)required-reviewerwith its path filter and the Entra ID group:az repos policy required-reviewer create --repository-id $REPO_ID --branch main --blocking true --enabled true --required-reviewer-ids "Contoso-DBA-Reservas" --path-filter "/src/Contoso.Reservas.Datos/Migraciones/*". (c)merge-strategyallowing only--allow-squash true. (d)policy buildwith--valid-duration 720, which invalidates builds older than 12 hours. - She cannot. Policies apply equally to project administrators unless the bypass-policies permission is explicitly granted, which Contoso does not do. That is precisely what turns the team's agreement into a real rule and what satisfies the auditor: there are no exceptions by job title.
Conclusion
Contoso Airlines' code no longer lives on a laptop or travels by email. You have gone over the Git essentials — commit, branch, merge and rebase, with the rule of never rebasing shared branches — you have created the project's four repositories and you have migrated Diego's local repository preserving its three years of history, leaving the old origin read-only so that two truths do not appear. You know how to authenticate with Git Credential Manager as the default, with SSH where it suits, and with personal access tokens only when there is no alternative, knowing that a PAT inherits every permission of whoever created it.
You have consciously chosen the branching strategy: trunk-based development with feature branches, with very short-lived branches, because Contoso has a single live version, needs review for PCI DSS and its weakest DORA metric is lead time. And you have turned the quality process into something that does not depend on anybody's memory: pull requests with a template, reviewers and a recorded conversation — which is, incidentally, the best architecture documentation the team will ever have — and policies on main requiring two approvals with no self-approval, a linked work item, resolved comments, a valid build validation and squash merge, with automatic reviewers by path that take every schema change to Contoso-DBA-Reservas and every template to Contoso-Infraestructura. You know how to tag with semantic versioning, how to maintain a serious .gitignore and, above all, you know that a pushed secret has to be rotated: deleting the commit deletes nothing, and the underlying solution is still module 4's, having no secrets to leak.
One very visible loose end remains. The most important policy you have configured — build validation — points at a pipeline that does not exist yet: today nobody checks that the code builds, that the tests pass or that no obvious bug has slipped in before merging. In the next lesson, Azure Pipelines: Continuous Integration, you will build that pipeline: hosted and self-hosted agents with their real costs, the anatomy of an azure-pipelines.yml explained line by line, restore, build, tests with published results and coverage, static analysis, caching, reusable templates so you do not repeat the same YAML in every service, and secrets coming in from Key Vault without ever passing through the repository. By the end of it, the rule "main always builds" will stop being an intention and become a fact verified on every change.
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
