Pick the role you are aiming at, build its one project, finish it, then take the cert. Six roles below: identity, cloud platform security, security operations, platform engineer, data engineer and AI cloud developer. Each has a build table, a "done looks like" checklist, the evidence to save, the CloudLearn labs for the pieces, and the three interview questions the project lets you answer. One finished project beats five certificates.
This page is role-specific on purpose. If you have no role in mind yet, start somewhere else.
🧭 How to pick

Find the row that matches the job you have today, not the one that sounds best. The skills overlap more than the job titles suggest, so you can switch later.
| Your job today | Role to aim at | The one project | Cert, after the project |
|---|---|---|---|
| Helpdesk, Microsoft 365 admin, sysadmin with Active Directory | Identity (security lane 1) | Conditional Access rolled out safely, admin roles in PIM | SC-300 |
| Azure admin, network engineer, engineer from AWS or Google Cloud | Cloud platform security (lane 2) | One app with no public endpoints, guarded by a Deny policy | SC-500 |
| SOC analyst moving from triage into engineering | Security operations (lane 3) | One Sentinel detection triggered on purpose, one KQL hunt, one automation rule | SC-200 |
| DevOps engineer, QA engineer or SDET, network engineer | Platform engineer | A paved road for one app: pipeline, module, GitOps, guardrails, self-service, measured | AZ-400 is optional |
| You already write SQL or Python against data | Data engineer | One dataset from bronze to gold, on a schedule, in Git | DP-700 (Fabric) or DP-750 (Databricks) |
| Back-end developer, or a developer moving into AI apps | AI cloud developer | A Python app that does vector search in Cosmos DB, with no keys anywhere | AI-200 |
✅ What makes a project count
Three tests. A project has to pass all three before it goes on your resume.
| Test | Counts | Does not count |
|---|---|---|
| Finished | Every "done looks like" box below is ticked | Four projects at seventy percent, or one that stops before the hard step |
| Deployed | It ran in a real Azure subscription or lab environment, and you broke it and fixed it at least once | Only on your laptop, or a diagram of what you would build |
| Written up | A README or short post with the design, what broke, and why you made each choice | A README that only lists setup commands |
And the things people often submit that do not count on their own:
| Not a project | Why |
|---|---|
| A completed guided lab | Labs teach the pieces. The project is you putting the pieces together without the steps in front of you |
| A certificate | It clears a filter. It does not give you anything to talk about for ten minutes |
| A tutorial repo you cloned | The interviewer asks one "why" and the answer is "the tutorial did it that way" |
Every project below is one system for one role. Each has a step that only exists to produce evidence: a blocked deployment, a triggered rule, a rollback, a rerun. That step is the one interviewers ask about. Do not skip it.
🔐 Identity: Conditional Access, rolled out safely
Proves: you can change how a whole organisation signs in without locking anyone out.
| Step | What you build |
|---|---|
| 1 | Two break-glass accounts, excluded from every Conditional Access policy |
| 2 | A policy that requires MFA for all users, in report-only mode |
| 3 | A sign-in log query showing who the policy would have blocked |
| 4 | Fix those gaps, then switch the policy to On |
| 5 | Admin roles made eligible in Privileged Identity Management (PIM), with approval and a time limit |
| 6 | A write-up of the rollout and the decisions behind it |
Step 3 is the part that proves judgement. With sign-in logs going to a Log Analytics workspace:
SigninLogs
| where TimeGenerated > ago(7d)
| mv-expand Policy = ConditionalAccessPolicies
| where tostring(Policy.result) startswith "reportOnly"
| summarize SignIns = count() by PolicyName = tostring(Policy.displayName), Result = tostring(Policy.result)
| order by PolicyName asc, SignIns descreportOnlyFailure rows are the people to fix before you switch it on.
Done looks like
- Two break-glass accounts, excluded from Conditional Access
- The MFA policy ran in report-only for at least a week
- A query or workbook showing what report-only caught
- The policy is On, with the gaps fixed first
- No standing admin roles: everything privileged is eligible in PIM
- A write-up exists
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
| The KQL result with the report-only failures, and the PIM activation request with approval | The KQL query and an export of the policy settings | Who report-only caught, what you fixed, why the break-glass accounts are excluded |
Labs that cover the pieces
| Lab | Covers |
|---|---|
| Configure Conditional Access Policies and MFA in Entra ID | Steps 2 to 4 |
| Entra ID: Create Users, Groups and Assign Roles | Users, groups and role assignments |
| Securing Azure Web Apps with Managed Identity and Key Vault | Workload identities, the SC-300 area people skip |
Three interview questions this lets you answer
- "How would you roll out MFA for everyone without locking anyone out on day one?"
- "What is a break-glass account, and how do you stop it becoming a risk of its own?"
- "Why make admin roles eligible in PIM instead of just assigning them?"
🛡️ Cloud platform security: no public endpoints
Proves: you can lock data services off the internet and stop the next person from undoing it.
| Step | What you build |
|---|---|
| 1 | A storage account, an Azure SQL database and a Key Vault in one resource group |
| 2 | Private endpoints for all three, with private DNS zones |
| 3 | Public network access disabled on all three |
| 4 | The built-in policy "Storage accounts should disable public network access", assigned with the effect set to Deny (its default is Audit) |
| 5 | A public storage account deployment that fails, captured |
| 6 | A write-up of the design, with Defender for Cloud recommendations before and after |
Steps 4 and 5 are the screenshot that does the talking:
az policy assignment create \
--name deny-public-storage \
--policy b2982f36-99f2-4db5-8eff-283140c09693 \
--scope "/subscriptions/<subscription-id>/resourceGroups/rg-sec-proof" \
--params '{ "effect": { "value": "Deny" } }'
# Should fail with RequestDisallowedByPolicy
az storage account create \
--name stpublictest$RANDOM \
--resource-group rg-sec-proof \
--public-network-access EnabledPolicy assignments can take a few minutes to apply. If the test deployment succeeds straight away, wait and retry before you debug the policy.
Done looks like
- Storage, SQL and Key Vault each have a private endpoint and DNS that resolves to it
- Public network access is disabled on all three
- A policy assignment with the Deny effect
- A real deployment blocked by that policy
- Defender for Cloud recommendations recorded before and after
- A write-up exists
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
The RequestDisallowedByPolicy error, and Defender for Cloud before and after | The CLI or Bicep that builds it, including the policy assignment | The network diagram, why each endpoint is private, what Defender for Cloud flagged |
Labs that cover the pieces
| Lab | Covers |
|---|---|
| Azure Private Endpoints: Lock Down Your Storage and SQL | Steps 2 and 3 |
| Deploying and Managing Azure Key Vault | Key Vault setup and access |
| Implement NSGs and ASGs in Azure | Network segmentation around it |
| Secure VM Access with Azure Bastion | Getting in to test without a public IP |

Azure Private Endpoints: Lock Down Your Storage and SQL
The middle of this project in a real Azure environment: private endpoints, private DNS and public access turned off.
Three interview questions this lets you answer
- "How do you make sure a storage account cannot be reached from the internet, and how would you prove it?"
- "What is the difference between an Audit policy and a Deny policy, and when do you use each?"
- "After you add a private endpoint, how does the app still find the service by name?"
🚨 Security operations: one detection, one hunt
Proves: you can write detections and hunt by hand, not only work the queue someone else built.
| Step | What you build |
|---|---|
| 1 | A Microsoft Sentinel workspace, onboarded to the Defender portal |
| 2 | At least one data connector, such as Entra ID sign-in logs |
| 3 | One custom analytics rule, triggered on purpose |
| 4 | One KQL hunt, documented even if it finds nothing |
| 5 | An automation rule that assigns and tags the incident |
| 6 | A write-up of the rule, the hunt, and how you would tune both |
Learn Sentinel in the Defender portal from day one. After March 31, 2027 it is no longer supported in the Azure portal.
A good first hunt is password spraying, one IP failing sign-ins against many accounts. Write your own before you look at this one:
SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType != "0"
| summarize FailedAccounts = dcount(UserPrincipalName), Attempts = count() by IPAddress
| where FailedAccounts >= 10
| order by FailedAccounts descDone looks like
- Workspace onboarded to the Defender portal
- At least one data connector sending data
- One custom analytics rule, with an incident you caused yourself
- One hunt written by hand and documented
- One automation rule firing on that incident
- A write-up exists
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
| The incident your rule raised, assigned and tagged by the automation rule | The rule query and the hunt query | What the rule detects, what the hunt looked for and found, what you would tune |
Labs that cover the pieces
Three interview questions this lets you answer
- "Walk me through a detection you wrote. How did you know it actually worked?"
- "How would you look for password spraying in our sign-in logs?"
- "One of your rules is flooding the queue with false positives. What do you do?"
🛣️ Platform engineer: the paved road
Proves: you can build a path to production that other engineers use without asking you, and show that it helped.
DevOps engineers usually start at step 2. QA engineers start at step 1. Network engineers learn Git and pull requests first, then step 1.
| Step | What you build | Proof artifact |
|---|---|---|
| 1. Walk the path | One app: repo, container, GitHub Actions pipeline to Container Apps, Application Insights | A green run, and a rollback you did on purpose |
| 2. Module | The infrastructure as a versioned Bicep module in a private registry in Azure Container Registry | Someone else deploys it from your README alone |
| 3. GitOps | The app on AKS, driven from Git by Flux or Argo CD | A merged pull request the cluster applies on its own |
| 4. Guardrails | AKS deployment safeguards, plus an Azure Policy on the Azure resources | A bad deployment blocked, with the error |
| 5. Self-service | A Backstage Software Template that creates a repo and calls your module | A new service created from one form |
| 6. Measure | DORA metrics before and after, plus feedback from one developer | A before and after table, and one change you made from the feedback |
Step 1 is not done until you have rolled back. In Container Apps with multiple-revision mode:
az containerapp ingress traffic set \
--name app-paved-road \
--resource-group rg-platform-proof \
--revision-weight app-paved-road--v1=100 app-paved-road--v2=0Step 4 starts on Warn, then moves to Enforce once you have fixed what the warnings showed:
az aks safeguards update \
--resource-group rg-platform-proof \
--name aks-platform-proof \
--level EnforceTwo services older roadmaps still recommend: Azure Deployment Environments retires on February 22, 2027, and AWS Proton support ended on October 7, 2026. This project uses neither. Details are in Microsoft's Deployment Environments retirement guide.
Done looks like
- One app deploys to Container Apps from a GitHub Actions pipeline
- A broken release rolled back on purpose
- The infrastructure is a versioned Bicep module in a private registry
- Someone else deployed it using only your README
- Changes reach the AKS cluster only through Git
- Deployment safeguards on
Enforce, with a blocked deployment captured - An Azure Policy denies at least one insecure Azure resource
- A Backstage template creates a new service from one form
- DORA metrics recorded before and after
- Feedback from one developer, and one change you made because of it
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
| The rollback, the blocked deployment, the Backstage form | The app repo, the module repo, the GitOps repo and the template | The before and after DORA table, the developer feedback, what you changed |
Labs that cover the pieces
| Lab | Covers |
|---|---|
| Containerize and Deploy to Azure Container Apps | Step 1, container and deploy |
| Deploy Containerized Apps to Azure with GitHub Actions | Step 1, the pipeline |
| Implement a Canary Release for Azure Container Apps | Step 1, revisions and rollback |
| Getting Started with Azure Application Insights | Step 1, monitoring |
| Publish and Consume Bicep Modules with ACR | Step 2 |
| Automate Bicep Deployments with GitHub Actions | Step 2, from a pipeline |
| Create and Debug Your First Pod with kubectl | Step 3, Kubernetes basics |
| Imperative vs Declarative Pod Creation | Step 3, why GitOps needs declarative config |
| Build Your First Helm Chart from Scratch | Step 3, packaging for the GitOps repo |
Steps 4 to 6 have no CloudLearn lab yet. Use the AKS deployment safeguards docs for step 4, and the Backstage and DORA documentation for steps 5 and 6.
Three interview questions this lets you answer
- "A release just went out broken. Talk me through how you get users back on the last good version."
- "How do you get teams to use the platform instead of building their own way around it?"
- "How would you know whether your platform actually made delivery better?"
🥇 Data engineer: one medallion project
Proves: you can take messy data from raw to useful, on a schedule, and explain every layer.
Pick one platform first. Count which of Microsoft Fabric or Azure Databricks the data engineering job ads near you name more often. The five skills underneath carry across either way.
| Step | What you build | Done looks like |
|---|---|---|
| 1. Dataset | A public dataset that updates regularly, such as city open data or transit trips | You can say what is in it and what is wrong with it |
| 2. Bronze | Raw files landed exactly as they arrive | A bronze table you never edit by hand |
| 3. Silver | Types fixed, duplicates removed, nulls handled | Data quality rules you can point to |
| 4. Gold | Aggregates an analyst would actually query | One or two gold tables answering real questions |
| 5. Schedule | The whole flow on a schedule | It runs without you |
| 6. Git | Notebooks and pipeline definitions in source control | A repo someone else could read |
| 7. Decisions | Why each layer is shaped the way it is | A short write-up |
Once the first load works, make silver incremental with MERGE so a rerun updates changed rows instead of rewriting everything. The core of it, as Spark SQL in the same notebook:
MERGE INTO silver_trips AS target
USING (
SELECT trip_id, trip_date, fare FROM (
SELECT trip_id,
to_date(pickup_datetime) AS trip_date,
CAST(fare AS DECIMAL(10,2)) AS fare,
ROW_NUMBER() OVER (PARTITION BY trip_id ORDER BY _ingested_at DESC) AS rn
FROM bronze_trips
WHERE _ingested_at > current_timestamp() - INTERVAL 1 DAY AND fare >= 0
) WHERE rn = 1
) AS source
ON target.trip_id = source.trip_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *Done looks like
- Bronze, silver and gold tables exist and are rebuilt from bronze alone
- Silver loads incrementally with
MERGE, and a rerun does not duplicate rows - The flow runs on a schedule without you
- One column is hidden from a reader role (OneLake security in Fabric, a column mask in Unity Catalog)
- Notebooks and pipeline definitions are in Git
- A write-up exists
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
| A scheduled run history with several green runs, and a gold table answering a question | The notebooks, the pipeline definition, the MERGE | What was wrong with the raw data, the quality rules in silver, who gold is for |
Labs that cover the pieces
CloudLearn has no Fabric or Databricks lab yet. Use the Fabric trial or Databricks Free Edition for the platform. The labs cover the Azure pieces underneath every data platform:
| Lab | Covers |
|---|---|
| Getting Started with Azure Storage Account | Where raw files land |
| Object Replication and Lifecycle Management | Keeping raw data without paying for it forever |
| Deploy Azure SQL Database with Terraform | A source database to ingest from, and somewhere to practise MERGE |
| Azure Private Endpoints: Lock Down Your Storage and SQL | Private access to data |
| Deploying and Managing Azure Key Vault | Connection secrets for pipelines |
| Introduction to KQL in Log Analytics | KQL, which DP-700 also tests |
Three interview questions this lets you answer
- "Why keep a bronze layer at all if you are going to clean the data anyway?"
- "Your pipeline failed halfway and you rerun it. How do you make sure you do not end up with duplicate rows?"
- "How would you stop analysts from seeing one sensitive column without building a second copy of the table?"
🤖 AI cloud developer: vector search with no keys
Proves: you can build the back end around a model: the container, the vector store, the queue, the identity and the monitoring. AI-200 does not test training a model, so neither does this project.
Every service here is in the AI-200 skills outline. The app is a small "search my documents" API in Python. Which embedding model you call is your choice. The project is everything around it.
| Step | What you build | Outline area |
|---|---|---|
| 1 | A Python API in a container, built and stored with Azure Container Registry Tasks | Containers |
| 2 | Deployed to Azure Container Apps with a managed identity, using revisions | Containers |
| 3 | A Cosmos DB for NoSQL account with vector search enabled, and a container created with a vector embedding policy and a vector index | Data |
| 4 | An ingestion worker: new documents go on a Service Bus queue, a worker stores text plus embeddings in Cosmos DB, and a KEDA rule scales it from zero | Connect, Containers |
| 5 | A search endpoint that runs a VectorDistance query and returns the top matches | Data |
| 6 | No keys anywhere: Cosmos DB data-plane role for the managed identity, the model key read from Key Vault through the same identity | Secure |
| 7 | OpenTelemetry tracing to Application Insights, and a KQL query that finds the slowest requests | Monitor |
| 8 | A write-up of the design and one thing that broke |
Steps 3 and 6, the parts that define "done":
# Turn on vector search for the account (can take about 15 minutes)
az cosmosdb update \
--resource-group rg-ai-proof \
--name cosmos-ai-proof \
--capabilities EnableNoSQLVectorSearch
# Give the Container App's managed identity read and write on the data
az cosmosdb sql role assignment create \
--account-name cosmos-ai-proof \
--resource-group rg-ai-proof \
--role-definition-id 00000000-0000-0000-0000-000000000002 \
--scope "/" \
--principal-id <app-principal-id>The search query. Always use TOP N, or the query costs more Request Units than it needs to:
SELECT TOP 5 c.title, VectorDistance(c.embedding, @queryVector) AS score
FROM c
ORDER BY VectorDistance(c.embedding, @queryVector)Once it works, the stretch goal is the comparison AI-200 actually asks about: try the same search in PostgreSQL with pgvector or in Azure Managed Redis, and write down when you would pick each.
Done looks like
- Image built with ACR Tasks and running in Container Apps
- A document dropped on the queue ends up searchable, with the worker scaling up from zero and back
- The search endpoint returns ranked results from a
VectorDistancequery - No connection string or account key in app settings or the repo
- One request traced end to end in Application Insights
- A KQL query you wrote yourself that answers "what is slow"
- A write-up exists
Evidence to save
| Screenshot | Repo | Write-up |
|---|---|---|
| The KEDA scale-out under a queue of messages, and one end-to-end trace | The app, the worker, the infrastructure, and the container's vector policy | Why Cosmos DB for this, how the app authenticates, what broke |
Labs that cover the pieces
| Lab | Covers |
|---|---|
| Create and Manage Azure Container Registry | Step 1 |
| Containerize and Deploy to Azure Container Apps | Step 2 |
| Implement a Canary Release for Azure Container Apps | Step 2, revisions |
| Deploy and Query Azure Cosmos DB with the NoSQL API | Steps 3 and 5, the SDK and queries |
| Container Apps KEDA Scaling with Service Bus Queue Workers | Step 4 |
| Build an Event-Driven Messaging Solution with Service Bus | Step 4, queues and dead-letter |
| Securing Azure Web Apps with Managed Identity and Key Vault | Step 6 |
| Getting Started with Azure Application Insights | Step 7 |
| Introduction to KQL in Log Analytics | Step 7, the query |
The vector search step itself has no CloudLearn lab yet. Microsoft's vector search in Cosmos DB for NoSQL page and the Python index and query guide cover it.

Container Apps KEDA Scaling with Service Bus Queue Workers
The ingestion worker from step 4: a queue fills up, the app scales out from zero, then back down.
Three interview questions this lets you answer
- "Why did you store the vectors in Cosmos DB and not PostgreSQL or Redis?"
- "How does your app get into the database without a key or a connection string?"
- "A user says search is slow. How do you find out which part is slow?"
⚠️ Where this goes wrong
| Symptom | What happened | Fix |
|---|---|---|
| Two projects at seventy percent | Started the second role before finishing the first | Pick one row in the table at the top, finish its checklist |
| Built it, cannot explain it | Followed lab steps and called it the project | Rebuild it without the steps open, then write it up |
| No evidence step | Skipped the blocked deployment, the triggered rule or the rollback | Do that step. It is the one you get asked about |
| Cert booked before the project | The exam felt like progress | Finish the checklist first. The scenario questions then read like your own notes |
| The write-up is setup commands | Wrote it at the end from memory | Note what broke while it breaks |
Pick your row, build the one project, tick every box, then book the exam. Come back to this page with the checklist open, not to read it again.