AZ-400 practice questions and answers
All 50 questions from Full Practice Test 1 for Designing and Implementing Microsoft DevOps Solutions, with the correct answer and a full explanation for each — including why the other options are wrong. Free to read, no signup.
What this set covers
Questions are weighted to match the official AZ-400 exam guide. The real exam is 40-60 (not published; Microsoft states "Most Microsoft Certification exams typically contain between 40-60 questions; however, the number can vary depending on the exam.") questions in 100 minutes with a pass mark of 700 / 1000.
- Design and implement processes and communications7 q · 13%
- Design and implement a source control strategy6 q · 13%
- Design and implement build and release pipelines27 q · 53%
- Develop a security and compliance plan6 q · 13%
- Implement an instrumentation strategy4 q · 8%
A platform team maintains about 40 small Bicep modules, such as a storage account and a subnet. Application teams in several subscriptions must reference these modules from their own Bicep files, pin an exact version, and get a new version only when they change the reference. Which approach fits BEST?
- APublish the modules to a private Bicep module registry in Azure Container Registry, reference them with br: paths and a version tag, and grant the application teams the AcrPull role.✓
- BPublish every module as a template spec in a shared resource group and have each application team reference the template spec resource ID.
- CAdd the platform repository to each application repository as a Git submodule and reference the module files by relative path.
- DPackage the .bicep files as a universal package in Azure Artifacts and download them in the pipeline before each deployment.
Correct answer: A — Publish the modules to a private Bicep module registry in Azure Container Registry, reference them with br: paths and a version tag, and grant the application teams the AcrPull role.
A private Bicep module registry stores each module version as an OCI artifact in Azure Container Registry; az bicep publish pushes a tagged version, consumers write module storage 'br:contoso.azurecr.io/bicep/modules/storage:v1.2.0' or a shorter alias from bicepconfig.json, and read access is one AcrPull assignment. Template specs are best for complete templates that are deployed as they are; using them here creates 40 Azure resources per version that you must place and secure in every environment. C makes every consumer carry the whole platform repository and turns a version bump into a submodule rebase. D moves files around but gives you no native module reference and no restore-time version resolution.
Create private registry for Bicep modulesA company deploys a web app with an Azure Pipelines classic release pipeline. Before the production stage runs, the release must wait 10 minutes so the previous stage can settle, then check the Azure Monitor alert rules for the app every 5 minutes for up to 1 hour. If an alert is still firing at the end of that hour, production must not be deployed. Which configuration meets the requirement, and what happens when the hour is over and an alert is still firing?
- AAdd a Query Azure Monitor alerts gate. Set the delay before evaluation to 10 minutes, the time between re-evaluation of gates to 5 minutes, and the timeout after which gates fail to 1 hour. When the timeout is reached the deployment is rejected and production is not deployed.✓
- BAdd a Query Azure Monitor alerts gate. Set the delay before evaluation to 1 hour and the time between re-evaluation of gates to 5 minutes. When the hour is over the stage is skipped and the release is marked as succeeded.
- CAdd a pre-deployment approval with a timeout of 1 hour. When the timeout expires the release continues to production and records a warning.
- DAdd a Query Azure Monitor alerts gate with the time between re-evaluation of gates set to 5 minutes and no timeout. The release stays in progress until an operator cancels it by hand.
Correct answer: A — Add a Query Azure Monitor alerts gate. Set the delay before evaluation to 10 minutes, the time between re-evaluation of gates to 5 minutes, and the timeout after which gates fail to 1 hour. When the timeout is reached the deployment is rejected and production is not deployed.
A gate has three settings: a delay before the first evaluation, a sampling interval that controls how often the gate is re-evaluated, and a timeout. All gates on the stage must succeed in the same sampling for the deployment to continue, and if that never happens before the timeout, the deployment is rejected, so the stage fails and nothing ships. Option B misuses the delay for the whole waiting window and is wrong about the result, because a stage whose gates never pass is not reported as succeeded. Option C is wrong because an approval that times out is rejected, never auto-approved. Option D is wrong because the timeout field is always set, so a gate cannot run forever.
Release deployment control using gatesA CI pipeline in one repository builds and publishes a container image as a pipeline artifact. A separate deployment pipeline in a different repository must start automatically after that CI pipeline finishes successfully on the main branch, and must consume the artifact it produced. Which approach meets the requirement with the LEAST operational overhead?
- AAdd a resources.repositories entry for the CI repository with a trigger on main, and check that repository out in the deployment pipeline.
- BAdd a scheduled trigger that runs the deployment pipeline every 15 minutes and exits early when no new artifact is found.
- CAdd a final step to the CI pipeline that queues the deployment pipeline through the REST API using a personal access token.
- DAdd a resources.pipelines entry for the CI pipeline with a trigger on the main branch, and download its artifact with the download step.✓
Correct answer: D — Add a resources.pipelines entry for the CI pipeline with a trigger on the main branch, and download its artifact with the download step.
A pipelines resource with a trigger block is the pipeline completion trigger: the downstream run starts only after the source pipeline completes successfully on a matching branch, and the artifacts of that resource are available to download by resource alias. A is the trap, because a repository resource trigger fires on commits pushed to that repository, so the deployment can start while the build is still running or even after a build that failed. B adds latency and wasted runs. C works but you now own a token that must be stored, scoped and rotated.
Trigger one pipeline after anotherA team pushes many small commits to main during the day. Every push starts a CI run, and the runs queue up behind the organization's single parallel job, so results arrive an hour late. The team confirms it only needs the newest set of changes validated, not every individual commit. Which change clears the queue at NO extra cost?
- ABuy additional Microsoft-hosted parallel jobs for the organization.
- BSet batch: true on the CI trigger for main.✓
- CRegister self-hosted agents on spare team machines and point the pipeline at that pool.
- DSplit the pipeline into more jobs so the stages run in parallel.
Correct answer: B — Set batch: true on the CI trigger for main.
With batch: true, Azure Pipelines waits until the running build finishes, then starts one run that covers all pushes that arrived while it was busy. Fewer runs are queued and nothing has to be purchased. Buying parallel jobs works but costs money every month. Self-hosted agents also cost money once you need more than the single free self-hosted parallel job, plus the machines must be patched and maintained. Splitting the pipeline into more jobs makes it worse, because each concurrent job consumes another parallel job slot the organization does not have.
Configure and pay for parallel jobsEight developers work in one repository and ship to production every day. They are starting a new checkout page that will take about three weeks to finish. The unfinished page must never be visible to customers, but the team does not want a large, painful merge at the end. Which branching approach BEST meets these requirements?
- ACreate a long-lived feature branch for the checkout page and merge it into main after three weeks.
- BAdopt GitFlow, with a develop branch and release branches, and merge the checkout work into develop only when it is finished.
- CMerge short-lived branches into main every day and keep the new page hidden behind a feature flag.✓
- DKeep the checkout work in a personal fork and rebase it onto main every night until it is done.
Correct answer: C — Merge short-lived branches into main every day and keep the new page hidden behind a feature flag.
Trunk-based development merges small changes into main at least once a day, so branches never drift far apart and merge conflicts stay small. A feature flag keeps the half-finished checkout page turned off in production, which decouples deploying the code from releasing the feature. The long-lived feature branch and the GitFlow develop branch both delay integration for three weeks, which is exactly the big merge the team wants to avoid. A personal fork with a nightly rebase still keeps the code out of main, so the rest of the team never builds or tests against it.
What is feature management?A company keeps a busy repository on GitHub. About 40 pull requests merge into main every day. Today main is protected by a classic branch protection rule with "Require status checks to pass" and "Require branches to be up to date before merging" enabled. Developers complain that they must update their branch and wait for a full CI run again almost every time somebody else merges. The platform team also wants the same protection on every branch named release/*, and wants to try a new rule on the repository before it starts blocking people. Which TWO actions should the team take? (Select TWO.)
- AKeep "Require branches to be up to date before merging" and add more self-hosted runners so the repeated builds finish faster.
- BReplace the classic rule with a repository ruleset that targets main and release/*, and use the Evaluate enforcement status to dry run new rules before enforcing them.✓
- CCreate one classic branch protection rule for each release branch and rely on the most specific pattern winning when two rules overlap.
- DEnable the merge queue for main so pull requests are grouped, built against the base branch plus the changes ahead of them, and merged automatically.✓
- EEnable auto-merge on every pull request so GitHub merges as soon as the required checks pass.
Correct answer: B, D — Replace the classic rule with a repository ruleset that targets main and release/*, and use the Evaluate enforcement status to dry run new rules before enforcing them. · Enable the merge queue for main so pull requests are grouped, built against the base branch plus the changes ahead of them, and merged automatically.
A repository ruleset can target several branch patterns at once, several rulesets layer together, and a ruleset can be set to Evaluate so it reports what it would have blocked without blocking anyone. Classic branch protection has neither feature: only the most specific matching rule applies and there is no dry run mode, which is why option C is more admin work with no way to test. The merge queue removes the need to keep every branch up to date, because the queue builds each pull request against the base branch plus the changes queued ahead of it, so one run covers the group. Option A pays for the same wasted builds, and option E does not help because auto-merge still waits for the up-to-date requirement, so the rebase loop stays.
An engineering manager uses Azure Boards. She wants a chart that shows how many bugs were in the Active state at the end of each week for the last six months, including sprints that are already closed. Which approach returns that historical trend?
- ARun a WIQL flat query for bugs where State = Active and export the result to CSV once a week.
- BQuery the Analytics service with OData against the WorkItemSnapshot entity set and group the daily snapshots by week.✓
- CRun a WIQL query for bugs and add an ASOF clause with the start date of the current sprint.
- DOpen the bugs query and use the Charts tab to add a stacked area chart grouped by State.
Correct answer: B — Query the Analytics service with OData against the WorkItemSnapshot entity set and group the daily snapshots by week.
The Analytics service stores a snapshot row for every work item for every day it existed, so an OData query over WorkItemSnapshot can return a real time series and be rolled up to weeks. WIQL only returns the current state of work items, so option A only builds history going forward and only if somebody remembers to run it. The ASOF clause in option C returns the state of the items at one single moment in the past, not a series of points. Query charts in option D are drawn from the same current-state result set, so they cannot show past sprints.
What is Analytics?A company runs a pool of about 20 self-hosted Azure Pipelines agents. Each agent is a separate Azure virtual machine built from an image by an automation script, and VMs are created and deleted every week as demand changes. Every agent must read the same secrets from one Azure Key Vault. The solution must store no credential anywhere and must need the LEAST administrative effort as VMs come and go. What should the team use?
- AEnable a system-assigned managed identity on each agent VM and grant each identity the Key Vault Secrets User role on the vault.
- BCreate one user-assigned managed identity, assign it to every agent VM at creation time, and grant that identity the Key Vault Secrets User role on the vault.✓
- CRegister one Microsoft Entra application, create a client secret for it, and store the secret in an Azure Pipelines variable group marked as secret.
- DEnable a system-assigned managed identity on each agent VM and have the startup script add a Key Vault access policy for that identity.
Correct answer: B — Create one user-assigned managed identity, assign it to every agent VM at creation time, and grant that identity the Key Vault Secrets User role on the vault.
A user-assigned managed identity is a standalone Azure resource with its own lifecycle, so one role assignment on the vault covers all 20 VMs, and the identity keeps working when a VM is deleted or a new one joins the pool. A system-assigned identity is created and deleted with its VM, so option A means a fresh role assignment for every new VM and an orphaned assignment for every deleted one. Option D has the same lifecycle problem and adds a fragile startup script that needs rights to change vault permissions, and access policies are the older model that Azure RBAC replaced. Option C stores a client secret that must be protected and rotated, which is exactly the credential the requirement rules out.
Managed identities for Azure resourcesA database password is stored in Azure Key Vault. A YAML pipeline must use it during deployment, and the value must never be written into the repository. A Bash step currently reads $DB_PASSWORD from the environment and gets an empty string. Which TWO actions produce a working and secure setup? (Select TWO.)
- bash: |
./deploy.sh --password "$DB_PASSWORD"
displayName: Deploy- AGive the service connection identity the Key Vault Contributor role so it can read secret values.
- BLink a variable group to the key vault through an Azure Resource Manager service connection whose identity has Get and List permissions on secrets, then reference the group in the pipeline.✓
- CMove the password into the YAML file as a variables entry so the script can read it directly.
- DMap the secret into the step with an env block, for example DB_PASSWORD: $(dbPassword).✓
- ETurn on system.debug so the value is written to the log and picked up by the script.
Correct answer: B, D — Link a variable group to the key vault through an Azure Resource Manager service connection whose identity has Get and List permissions on secrets, then reference the group in the pipeline. · Map the secret into the step with an env block, for example DB_PASSWORD: $(dbPassword).
A variable group linked to a key vault pulls the selected secret values at queue time through the service connection, so nothing is stored in Git and the AzureKeyVault task is not needed. The second half is the classic trap: secret variables are not decrypted into the environment of script, bash or pwsh steps, so $DB_PASSWORD stays empty until you map it with an env block on that step. A grants the wrong permission, because Key Vault Contributor manages the vault but gives no access to secret values; the identity needs Get and List on secrets, through Key Vault Secrets User or an access policy. C puts the password in source control. E writes a secret to a log that many people can read.
Link secrets from an Azure key vault to a variable groupA platform team builds a shared npm package in a GitHub Actions workflow. The package is consumed by other GitHub Actions workflows and by Azure Pipelines in an Azure DevOps organization. The team also wants public packages from npmjs.com cached in the same feed so builds keep working during an npmjs.com outage. Which approach meets all of the requirements with the LEAST operational overhead?
- APublish to an Azure Artifacts feed from the workflow by using a personal access token scoped to Packaging (Read & write) stored as a GitHub secret, and enable an npmjs.com upstream source on the feed.✓
- BPublish to GitHub Packages by adding packages: write to the job permissions, and let Azure Pipelines restore from it with the same GITHUB_TOKEN.
- CPublish to an Azure Artifacts feed from the workflow by using a personal access token scoped to Code (Read & write) stored as a GitHub secret.
- DPublish to GitHub Packages, then run a nightly Azure Pipelines job that downloads every new version and republishes it to an Azure Artifacts feed.
Correct answer: A — Publish to an Azure Artifacts feed from the workflow by using a personal access token scoped to Packaging (Read & write) stored as a GitHub secret, and enable an npmjs.com upstream source on the feed.
Azure Artifacts serves both consumers from one feed and is the only option here that offers upstream sources, which proxy and cache npmjs.com packages so builds survive an outage. Publishing to it from outside Azure DevOps needs a personal access token with the Packaging (Read & write) scope. Option B fails because GITHUB_TOKEN is minted for a single GitHub Actions run and cannot be used by Azure Pipelines, and GitHub Packages has no npm upstream caching. Option C uses the Code scope, which grants repository access and no packaging rights, so the publish is rejected. Option D adds a second registry and a nightly sync job to keep alive, which is the most overhead, not the least.
Get started with Azure ArtifactsA product owner from a partner company joins a project. She must add and edit user stories on the backlog of a private Azure Boards project, and she must label and close issues in the team's private GitHub repository. She must not consume a paid Azure DevOps license, and she must not be added to the GitHub organization. Which TWO actions meet the requirements with the LEAST privilege? (Select TWO.)
- AAssign her Stakeholder access in the Azure DevOps organization and add her to the project's Contributors group.✓
- BAssign her Basic access in the Azure DevOps organization and add her to the project's Contributors group.
- CInvite her to the private GitHub repository as an outside collaborator with the Triage role.✓
- DAdd her to the GitHub organization as a member and grant her the Write role on the repository.
- EAssign her Stakeholder access in the Azure DevOps organization and add her to the Project Administrators group.
Correct answer: A, C — Assign her Stakeholder access in the Azure DevOps organization and add her to the project's Contributors group. · Invite her to the private GitHub repository as an outside collaborator with the Triage role.
Stakeholder access is free and unlimited, and in a private project it still allows a user to view, add, and edit work items on backlogs and boards, which is all a product owner needs; the Contributors group supplies the matching permission. Basic access is a paid license, so option B fails the cost requirement. An outside collaborator has repository access without joining the organization, and the Triage role allows labelling and closing issues without any write access to code. Option D adds her to the organization, which the requirement forbids, and grants more than she needs. Option E makes her a project administrator, far beyond least privilege.
About access levelsA monorepo is several gigabytes with more than ten years of history. A CI pipeline that builds only the services/checkout folder runs on a self-hosted agent pool, and the checkout step alone takes about nine minutes. The build does not need history or any other folder. Which TWO changes will cut the checkout time the MOST? (Select TWO.)
steps: - checkout: self
- ASet fetchDepth: 1 on the checkout step so the agent fetches only the newest commit instead of the full history.✓
- BSet sparseCheckoutDirectories: services/checkout on the checkout step so only that folder is written to the workspace.✓
- CSet submodules: recursive on the checkout step.
- DSet clean: true so the agent deletes the workspace before every run.
- ERemove the checkout step and run scalar clone in a script step instead.
Correct answer: A, B — Set fetchDepth: 1 on the checkout step so the agent fetches only the newest commit instead of the full history. · Set sparseCheckoutDirectories: services/checkout on the checkout step so only that folder is written to the workspace.
fetchDepth: 1 makes a shallow fetch, so the agent pulls one commit rather than a decade of objects, and sparseCheckoutDirectories uses Git cone-mode sparse checkout so only the folder the build needs is materialised on disk. C pulls extra repositories and makes the checkout slower. D throws away the local object cache on a self-hosted agent, which forces a full re-fetch every run and is the opposite of what is wanted. E is aimed at long-lived developer clones with background maintenance; on a build agent it adds work that the built-in checkout options already do.
Configure Git repository options in Azure PipelinesA team wants a card in one Microsoft Teams channel only when the nightly-build pipeline fails on the main branch. Runs on feature branches and successful runs must stay out of the channel. The team does not want to store or rotate any credentials. Which solution meets the requirement with the LEAST operational overhead?
- ACreate a service hook subscription that posts every run state change to a Teams webhook and let readers ignore the noise.
- BAdd a final pipeline step with condition failed() that posts a message to a Teams webhook URL stored as a secret variable.
- CInstall the Azure Pipelines app in the channel, subscribe to the pipeline, then set the subscription filters to build result Failed and branch main.✓
- DAdd the channel email address as a recipient of a project-level notification subscription for all completed builds.
Correct answer: C — Install the Azure Pipelines app in the channel, subscribe to the pipeline, then set the subscription filters to build result Failed and branch main.
The Azure Pipelines app for Microsoft Teams subscribes a channel to a pipeline and lets you filter the subscription by result and by branch, so only failed main-branch runs post a card, and the app handles authentication for you. A sends everything and pushes the filtering onto humans. B works but you now own a webhook secret, the posting code and the condition, and a job that dies on the agent may never reach that step. D emails the channel for every completed build, success included, so the noise problem stays.
Retention policies for builds, releases, and testsA company keeps its source code in GitHub and tracks work in Azure Boards. The Azure Boards app for GitHub is installed and the repository is already connected to the Azure DevOps project. Developers must not have to open Azure Boards to update status. When a pull request that fixes a bug is merged into the default branch, the linked work item must move to Done on its own. Which action meets the requirement with the LEAST manual effort?
- APut AB#4521 in the pull request title, then merge the pull request.
- BPut Fixes AB#4521 in the pull request description or in the commit message.✓
- CPut #4521 in the commit message so that Azure Boards picks up the work item ID.
- DOpen the work item, use Development > Create a branch to create a GitHub branch named after the work item, and merge that branch.
Correct answer: B — Put Fixes AB#4521 in the pull request description or in the commit message.
The AB# mention on its own only creates a link between the pull request and the work item. To change the state you must add a keyword in front of the mention - fix, fixes, fixed, close, closes, closed, resolve, resolves or resolved - and the work item moves to the Done or Completed state when the commit or pull request lands on the default branch. Option A links the item but never transitions it. Option C is the Azure Repos mention style; in GitHub a bare # is read as a GitHub issue reference, so nothing reaches Azure Boards. Option D creates a branch link, and branch links never change work item state.
Link GitHub commits, pull requests, branches, and issues to work itemsA team publishes NuGet packages to an Azure Artifacts feed from a CI pipeline, and every build publishes a new version. Other teams keep pulling versions that have not passed integration tests. The feed also has a retention policy that keeps only the 30 most recent versions, and a well tested package was deleted last month. Which approach fixes both problems with the LEAST operational overhead?
- AKeep publishing to the same feed, promote a package to the @release view only after integration tests pass, and have consumers use the @release view URL as their package source.✓
- BCreate a second feed named Release and add a pipeline task that copies tested packages into it, then point all consuming teams at the second feed.
- CGive the consuming teams Reader access to the feed and give only the publishing team Contributor access.
- DAdd the CI feed as an upstream source on each consuming team's own feed, and raise the retention policy on the CI feed so it keeps 500 versions instead of 30.
Correct answer: A — Keep publishing to the same feed, promote a package to the @release view only after integration tests pass, and have consumers use the @release view URL as their package source.
Every package a pipeline publishes lands in the @local view. Promoting a version to @release after the tests pass is the quality gate, and consumers who point at the feed's @release view can only restore promoted versions. Promotion also protects the package, because retention policies never delete a package that has been promoted to a view, so the accidental deletion problem is solved by the same change. Option B reaches the same goal but adds a second feed, a copy step and a second retention policy to manage. Option C controls who may publish, not which versions consumers can see. Option D leaves every untested version visible and only delays deletion.
Views on Azure Artifacts feedsA YAML pipeline sets a variable in a script and then uses it in the condition of a later job. The Test job is always skipped, even when the script clearly prints the value true. Which change makes the Test job run when the variable is true?
jobs:
- job: Build
steps:
- script: echo "##vso[task.setvariable variable=runTests;isOutput=true]true"
name: setVars
- job: Test
dependsOn: Build
condition: ${{ eq(variables.runTests, 'true') }}
steps:
- script: dotnet test- AChange the condition to: condition: eq(dependencies.Build.outputs['setVars.runTests'], 'true')✓
- BChange the condition to: condition: eq($(runTests), 'true')
- CChange the condition to: condition: ${{ eq(variables['runTests'], 'true') }}
- DDeclare runTests under a variables block at the top of the pipeline and keep the condition unchanged.
Correct answer: A — Change the condition to: condition: eq(dependencies.Build.outputs['setVars.runTests'], 'true')
Template expressions written as ${{ }} are evaluated when the YAML is compiled, before any job starts, so runTests does not exist yet and the condition compiles to false forever. A runtime expression reading the output variable through dependencies solves it, because job conditions are evaluated at run time after the Build job finishes. Option B uses macro syntax $( ), which is replaced just before a task runs and is not available for job conditions. Option C is the same compile-time syntax with different index notation, so it fails in exactly the same way. Option D would give the variable an empty compile-time value, and the value set by the script would still be invisible to the compile-time expression.
Expressions in Azure PipelinesA CI pipeline runs about 4,000 unit tests with the Visual Studio Test task on one Microsoft-hosted agent and takes 40 minutes. A compliance rule says every CI run must execute the complete test suite. The organization already owns several parallel jobs. Which change gives the team the FASTEST feedback without breaking the compliance rule?
- ATurn on Test Impact Analysis in the Visual Studio Test task so only the tests affected by the changed code run.
- BSet batch: true on the CI trigger so several pushes are validated by a single run.
- CMove the tests into a scheduled nightly pipeline and leave only compilation in CI.
- DRun the test job on several agents with a parallel job strategy and let the Visual Studio Test task slice the tests across them.✓
Correct answer: D — Run the test job on several agents with a parallel job strategy and let the Visual Studio Test task slice the tests across them.
A job with a parallel strategy runs on several agents at once, and the Visual Studio Test task slices the test assemblies across those agents, so all 4,000 tests still run but wall-clock time drops roughly in line with the agent count. The trade-off is cost: each slice consumes one parallel job. Test Impact Analysis is faster still, but it deliberately skips tests it judges unaffected, which breaks the rule that every run executes the full suite. Batching only reduces how many runs start, not how long one run takes. Moving tests to a nightly pipeline removes the feedback from CI entirely.
Run tests in parallel using the Visual Studio Test taskA team ships a product from a GitHub repository. Every change reaches the default branch through a pull request, and each pull request is labelled feature, bug, or docs. For each tag, the team wants release notes that list the merged pull requests grouped under "New features", "Bug fixes" and "Documentation". Developers must not be asked to change the way they write commit messages. Which approach meets the requirement with the LEAST effort?
- AAdd a workflow step that runs git log between the two tags and writes the output into the release body.
- BAdd a pipeline job that runs semantic-release, and add a commit-message hook that enforces Conventional Commits so the tool can group commits by type.
- CAdd a .github/release.yml file that defines the three categories and maps each one to its pull request label, then create the release with GitHub's automatically generated release notes.✓
- DUse the GitHub Release pipeline task with the release notes source set to the changes since the last release, then edit the generated text by hand before publishing.
Correct answer: C — Add a .github/release.yml file that defines the three categories and maps each one to its pull request label, then create the release with GitHub's automatically generated release notes.
GitHub's automatically generated release notes read the merged pull requests between two tags, and a .github/release.yml file sorts them into named categories based on pull request labels and authors. Because the grouping comes from labels, nothing about commit messages has to change. Option B works but forces Conventional Commits on every developer and adds a release tool to maintain, which is exactly the change the team ruled out. Option A produces one flat list with no grouping. Option D produces notes, but they are not grouped by label and someone must edit every release by hand.
A company wants to release a new checkout page without a new deployment. The team plans to turn it on for 10 percent of signed-in users first, and each of those users must keep seeing the same version on every visit. The app runs on 12 Azure App Service instances and reads its feature flags from Azure App Configuration. All instances must pick up a flag change together, and the app must not call App Configuration on every request. What should the team do?
- AUse a feature flag with the Percentage filter set to 10, and register every feature flag for refresh with a cache expiration of 1 second.
- BUse a feature flag with the Targeting filter and a 10 percent default rollout, register a sentinel key for refresh in the App Configuration provider, and update the sentinel key after every flag change.✓
- CStore the flag value in App Service application settings and move it into production with a deployment slot swap.
- DUse a feature flag with the Time Window filter that opens at the release time, and restart the App Service instances so they read the new value.
Correct answer: B — Use a feature flag with the Targeting filter and a 10 percent default rollout, register a sentinel key for refresh in the App Configuration provider, and update the sentinel key after every flag change.
The Targeting filter buckets each user by their user ID, so the same user always lands on the same side of the 10 percent line and the experience is stable between visits. The sentinel key pattern gives the consistent switch: the provider watches one key, you change the flags first and the sentinel key last, so on the next refresh every instance sees a complete set of changes with a single request instead of polling each key. Option A fails because the Percentage filter re-rolls on each evaluation, so one user can see the new page and then the old one, and a one second cache means constant calls to App Configuration. Option C needs a deployment, which is what the team wanted to avoid. Option D turns the feature on for everybody at once and a restart is exactly the disruption the team is trying to remove.
Use dynamic configuration in an ASP.NET Core appA company hosts its code on GitHub. Two problems reached production last month. First, a developer committed an Azure Storage account key and nobody noticed for days; the team wants the next attempt stopped at git push time. Second, a known vulnerability in a package that the app pulls in indirectly shipped unnoticed; the team wants to be told when any package it depends on gets a published security advisory. Which TWO features should the team enable? (Select TWO.)
- ASecret scanning with push protection.✓
- BCode scanning with CodeQL on every pull request.
- CDependabot alerts with the dependency graph.✓
- DA required status check that runs npm audit after every merge to main.
- EA pre-receive hook that rejects any commit larger than 1 MB.
Correct answer: A, C — Secret scanning with push protection. · Dependabot alerts with the dependency graph.
Push protection is the only one of these that acts during git push: when a supported credential pattern such as an Azure Storage account key is detected, the push is blocked and the secret never lands in the history. Dependabot alerts read the dependency graph, which includes indirect dependencies, and raise an alert whenever a published advisory matches a version the repository uses. CodeQL analyses your own source for vulnerable patterns such as injection, and it runs after the code is pushed, so it neither blocks the push nor tracks advisories for third-party packages. Option D runs after the merge, so the vulnerable package is already on main, and it says nothing about the leaked key. Option E blocks large files, not secrets.
A developer commits a file that contains an Azure Storage account connection string to an Azure Repos Git repository. The commit is three weeks old and ten people have cloned the repository since then. A later commit deleted the file, but the security team says the repository is still exposed. Which TWO actions should the team take to remove the exposure? (Select TWO.)
- AAdd the file to .gitignore and push the change to main.
- BUse git filter-repo or BFG Repo-Cleaner to strip the secret from every commit, then force push the rewritten branches and tags and have everyone re-clone.✓
- CRevert the commit that added the file so the change is undone in the history.
- DRegenerate the storage account key so the leaked connection string stops working, and store the new value in Azure Key Vault.✓
- ETurn on secret scanning push protection so future pushes that contain secrets are blocked.
Correct answer: B, D — Use git filter-repo or BFG Repo-Cleaner to strip the secret from every commit, then force push the rewritten branches and tags and have everyone re-clone. · Regenerate the storage account key so the leaked connection string stops working, and store the new value in Azure Key Vault.
Deleting a file only adds a new commit; the old blob is still reachable by commit SHA, so anyone with a clone can still read the secret. Rewriting history with git filter-repo or BFG removes the blob from every commit, and the rewritten branches and tags must be force pushed while everyone re-clones, because old clones keep the original objects. Rewriting alone is never enough, so the credential must also be rotated - regenerating the storage account key makes the leaked string useless no matter who copied it. Option A only stops future commits of that path. Option C creates yet another commit and leaves the secret in history. Option E is good prevention for next time but does nothing about the secret already committed.
Branch policies and settingsA company is moving a classic release pipeline to a YAML pipeline in Azure DevOps. The classic pipeline reuses two task groups, deploys to a deployment group of on-premises servers, and has a pre-deployment approval on the production stage. Which statement correctly describes the migration?
- AThe task groups become variable groups, the deployment group becomes a self-hosted agent pool, and the approval is declared with an approval property on the production stage.
- BThe task groups are referenced from YAML with the taskGroup keyword, the deployment group is referenced with the deploymentGroup keyword, and the approval is inherited from the classic release definition.
- CThe task groups become YAML templates, the deployment group becomes a container resource, and the approval is replaced by a condition expression on the production stage.
- DThe task groups become YAML templates, the deployment group becomes an environment with virtual machine resources, and the approval must be re-created as an approval check on that environment because approvals cannot be declared in the YAML file.✓
Correct answer: D — The task groups become YAML templates, the deployment group becomes an environment with virtual machine resources, and the approval must be re-created as an approval check on that environment because approvals cannot be declared in the YAML file.
Task groups are a classic-only feature and cannot be called from YAML, so the reusable steps have to be re-written as templates. Deployment groups map to an environment with virtual machine resources, where each server registers with the same style of agent registration script. Approvals and checks are the capability with no YAML equivalent: they are configured on the resource by the resource owner in the UI, which is what keeps a pipeline author from approving their own deployment. Options A and B name keywords that do not exist, and a condition in option C only tests whether earlier stages succeeded, so it never pauses for a person.
Create and target an environmentA .NET pipeline uses the steps below. The unit tests pass and the test results appear, but the Code Coverage tab reports 0% and the coverage status check blocks every pull request. Which change fixes the reported coverage?
- task: DotNetCoreCLI@2
inputs:
command: test
arguments: '--configuration Release --collect:"XPlat Code Coverage"'
- task: PublishCodeCoverageResults@2
inputs:
summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml'- AChange summaryFileLocation to '$(Agent.TempDirectory)/**/coverage.cobertura.xml', where the test task writes the Cobertura file.✓
- BAdd failIfCoverageEmpty: true to the publish task so the real number is reported.
- CReplace PublishCodeCoverageResults@2 with PublishTestResults@2 and point it at the .trx file.
- DAdd --no-build to the test arguments so the collector attaches to the already compiled assemblies.
Correct answer: A — Change summaryFileLocation to '$(Agent.TempDirectory)/**/coverage.cobertura.xml', where the test task writes the Cobertura file.
The DotNetCoreCLI test task sends results to $(Agent.TempDirectory) by default, so the Cobertura file lands there and the glob under the sources directory matches nothing; the publish task uploads no data and coverage stays at 0%. Fix the path, or add --results-directory to the test arguments so both steps agree. B only makes the run fail more loudly, it does not find the file. C publishes pass and fail counts, not coverage. D changes how the tests build and has no effect on where the coverage file is written.
Review code coverage resultsA company hosts a repository in Azure Repos Git. The YAML file below is saved in that repository and is used by a pipeline. Pushes to main queue a build as expected, but opening a pull request that targets main never queues a build. What should the team do?
trigger:
branches:
include:
- main
pr:
branches:
include:
- main
pool:
vmImage: ubuntu-latest
steps:
- script: dotnet build
- AAdd batch: true under the pr section of the YAML file.
- BIn the pipeline settings, clear the Override the YAML pull request trigger from here option.
- CCreate a build validation branch policy on main that runs this pipeline.✓
- DGrant the project Build Service account the Contribute permission on the repository.
Correct answer: C — Create a build validation branch policy on main that runs this pipeline.
PR triggers declared in YAML are not supported for Azure Repos Git. The pr block is simply ignored there; it only works for GitHub and Bitbucket Cloud repositories. For Azure Repos you get pull request builds by adding a build validation policy to the target branch, which links the branch to the pipeline and queues it for every pull request. Option A changes nothing, because batching applies to CI triggers. Option B is a setting that only appears for GitHub and Bitbucket repositories. Option D controls whether the pipeline can write back to the repository and has no effect on triggering.
Build Azure Repos Git repositoriesA team restores npm packages straight from the public registry and builds fail whenever that registry has an outage. An engineer creates an Azure Artifacts feed, turns on the npmjs upstream source and points every project at the feed. Which statement about the new setup is TRUE?
- AAdding the upstream source mirrors every version of every package from the public registry into the feed, so all restores keep working.
- BThe feed forwards each request to the public registry every time, so nothing restores while that registry is down.
- CThe feed saves a copy of a package version the first time it is restored through the upstream, so versions the team has used before keep resolving; a version never restored before still has to come from the public registry and fails.✓
- DUpstream sources protect NuGet and Maven feeds only, so npm restores always go straight to the public registry.
Correct answer: C — The feed saves a copy of a package version the first time it is restored through the upstream, so versions the team has used before keep resolving; a version never restored before still has to come from the public registry and fails.
An Azure Artifacts upstream source saves a copy of each package version into the feed the first time someone restores it, and every later restore is served from the feed, which is what carries builds through an outage. A is the common misconception: nothing is pre-mirrored, the feed fills up on demand. B describes a plain proxy, not the save behaviour. D is wrong because upstream sources cover npm, NuGet, PyPI, Maven and Cargo. Removing the upstream later blocks any new package or version from being pulled, so pin your versions and warm the feed before you rely on it.
Upstream sources overviewA repository has years of history that contains large .psd design files. An engineer runs the commands below and force-pushes the rewritten history. A designer then clones the repository on a laptop that has Git installed but NOT the Git LFS extension. What is the MOST accurate description of what the designer sees?
git lfs install git lfs track "*.psd" git add .gitattributes git commit -m "Track PSD files with LFS" git lfs migrate import --include="*.psd" --everything git push --force origin --all
- AThe clone fails, because Git cannot resolve the LFS pointers without the extension.
- BThe clone succeeds and the .psd files arrive at full size, because .gitattributes tells Git where to fetch them.
- CThe clone succeeds but the .psd files are absent from the working tree.
- DThe clone succeeds and each .psd is a small text file that holds the LFS pointer, with a version line, an oid and a size, instead of the real image.✓
Correct answer: D — The clone succeeds and each .psd is a small text file that holds the LFS pointer, with a version line, an oid and a size, instead of the real image.
git lfs migrate import rewrites history so the committed content of every matching file is a short pointer file, while the real bytes move to the LFS store. The smudge filter that swaps the pointer for the real file is part of the Git LFS extension, so a client without it clones fine and checks out the pointer text itself, which is why apps report a corrupt image. A is wrong because plain Git has no reason to fail on a small text blob, and C is wrong because the pointer file is present and tracked. B describes what happens only after the designer installs git-lfs and pulls. Note that the force-push also changes every commit ID, so the whole team must re-clone.
Manage and store large files in GitA company keeps a long-lived branch named release/2026.07 for the version now running in production. Development continues on main, which already holds several features that are not approved for release. A serious production bug is reported. The fix is a single commit that is already merged into main. The release pipeline has a 90-minute performance test stage and a 10-minute deploy stage. The fix must reach production as FAST as possible without shipping the unapproved features. What should the team do?
- ACherry-pick the fix commit from main onto release/2026.07 through a pull request, then run the release pipeline with a runtime parameter that skips the performance test stage.✓
- BMerge main into release/2026.07, resolve any conflicts, and then run the full release pipeline including the performance test stage.
- CRebase release/2026.07 onto main and deploy the result to production.
- DDeploy directly from main now, and remove the unapproved features with follow-up commits after the incident is closed.
Correct answer: A — Cherry-pick the fix commit from main onto release/2026.07 through a pull request, then run the release pipeline with a runtime parameter that skips the performance test stage.
A cherry-pick copies only that one commit onto the release branch, so the unapproved work on main stays where it is, and the pull request keeps the branch policies and review trail intact. A boolean runtime parameter combined with a stage-level condition lets the hotfix run skip the 90-minute performance stage, which is the only way to hit the deploy stage quickly. Options B and C both bring every unreleased commit from main into the release branch, which is exactly what the team must avoid, and a rebase also rewrites a shared branch. Option D ships the unapproved features to customers and then tries to unpick them, which is slower and far riskier.
Specify conditionsA team protects the main branch in Azure Repos with a policy that requires two reviewers. A developer gets two approvals on a pull request, then pushes three more commits to the same pull request and completes it. The three new commits were never reviewed. The team must make sure that any new commit invalidates the earlier approvals. What should the team do?
- ARaise the minimum number of required reviewers from 2 to 4.
- BAdd a build validation policy and set the build expiration to expire immediately when main is updated.
- CClear the Allow requestors to approve their own changes option on the branch policy.
- DIn the Require a minimum number of reviewers policy, set When new changes are pushed to Reset all approval votes.✓
Correct answer: D — In the Require a minimum number of reviewers policy, set When new changes are pushed to Reset all approval votes.
The minimum reviewers policy has a When new changes are pushed setting. Choosing Reset all approval votes clears every Approved and Approved with suggestions vote as soon as a new commit is pushed, so reviewers must look at the pull request again before it can complete. Option A only adds more people who can approve code that later changes. Option B expires the build, not the votes, and a green build is not a code review. Option C stops authors from approving their own pull request, which is a different problem.
Branch policies and settingsA team uses a Kanban board in Azure Boards. Items take about three weeks from start to Done, but the team believes most of that time is spent waiting, not working. The team wants to see how long items sit in each stage of the board, and it wants to compare that with the total time from creation to Done. Which configuration gives the team this view with the LEAST custom reporting work?
- AAdd the Sprint Burndown widget to the team dashboard and shorten the sprint length to one week.
- BAdd the Velocity widget to the team dashboard and turn on Show bugs on the backlog.
- CSplit each in-progress board column into Doing and Done, then add the Cumulative Flow Diagram widget along with the Cycle Time and Lead Time widgets.✓
- DAdd the Lead Time widget only, and define a Definition of Done for every board column.
Correct answer: C — Split each in-progress board column into Doing and Done, then add the Cumulative Flow Diagram widget along with the Cycle Time and Lead Time widgets.
Splitting a column into Doing and Done makes queue time visible: an item that is finished but not yet pulled forward now sits in a Done sub-column instead of hiding inside the work column. The Cumulative Flow Diagram then shows how wide each band grows over time, and a widening band is exactly where work is idle. The Cycle Time widget measures the first In Progress column to Closed, while the Lead Time widget measures Created to Closed, so the gap between them is time spent waiting in the backlog. Sprint Burndown (A) only shows remaining work in one sprint and Velocity (B) only shows throughput per sprint, so neither exposes waiting. Option D gives a single total with no per-column detail.
Cycle time and lead time widgetsA web API is instrumented with Application Insights and adaptive sampling is enabled with the default settings. About 1 request in 200 to the /checkout endpoint returns HTTP 500, and the failures come and go. The team must find which downstream dependency causes the failures, with the LEAST effort. Which approach should the team use?
- AOpen the Failures view for /checkout, select a failed sample, and read the end-to-end transaction details to see the dependency call that returned the error; confirm the pattern on the Application Map.✓
- BIn Logs, count the rows in the requests table where success is false and count the rows in the dependencies table where the result code is 500, then treat the dependency with the higher count as the cause.
- CTurn off adaptive sampling in the SDK, redeploy the application, and wait a week for complete telemetry before starting the investigation.
- DEnable Application Insights Profiler on the app and read the CPU traces collected for the checkout endpoint.
Correct answer: A — Open the Failures view for /checkout, select a failed sample, and read the end-to-end transaction details to see the dependency call that returned the error; confirm the pattern on the Application Map.
The end-to-end transaction details view stitches together the failed request and every dependency, trace and exception that shares the same operation ID, so the failing SQL call or HTTP dependency and its result code are visible in one screen, and the Application Map shows the same relationship for the whole app with a failure rate on each edge. Sampling does not break this, because adaptive sampling keeps or drops a whole operation together, so a sampled-in transaction is still complete. Option B is the trap: sampling drops whole operations, so a raw row count under-reports reality; you have to use summarize sum(itemCount) to get estimated counts, and raw counts from two tables sampled at different rates cannot be compared. Option C throws away the cost saving and delays the fix by a week. Option D profiles CPU usage, which does not explain a dependency returning errors.
Application Map: Triage distributed applicationsA platform team publishes a shared .NET library to an Azure Artifacts feed. Consumer projects reference it with a floating version, so they pick up new builds automatically. The next release removes two public methods, which will break anyone who calls them. The team also wants to hand early builds to two pilot teams. No consumer may be moved to the breaking build automatically, and the early builds must not reach any team that did not ask for them. Which versioning approach meets the requirement?
<PackageReference Include="Contoso.Shared" Version="1.*" />
- APublish the breaking build as 1.5.0 and mark the removed methods with the Obsolete attribute.
- BSwitch to CalVer: publish the breaking build as 2026.8.0 and the pilot builds as 2026.8.1, and ask every consumer to pin an exact version.
- CPublish the breaking build as 2.0.0 and the pilot builds as 2.0.0+beta.
- DPublish the breaking build as 2.0.0 and the pilot builds as 2.0.0-beta.1.✓
Correct answer: D — Publish the breaking build as 2.0.0 and the pilot builds as 2.0.0-beta.1.
Semantic versioning says a breaking change raises the MAJOR number, so a 1.* floating reference will never resolve to 2.0.0 and no consumer is upgraded by accident. A prerelease label such as -beta.1 is skipped during restore unless a project explicitly opts in to prerelease versions, so the pilot builds stay with the pilot teams. Option A keeps the package inside the 1.* range, so every consumer gets a build that no longer compiles. Option C fails because everything after the plus sign is build metadata: NuGet ignores it, so 2.0.0+beta is treated as the stable 2.0.0 release. Option B stops the automatic upgrade only after every consumer edits their project file, and the pilot build 2026.8.1 is a normal stable version that anyone can pick up.
Package versioningAn engineer writes the pipeline below. The Build job creates a container image tag, and the Deploy job must use the same tag. When the pipeline runs, the Deploy job prints an empty value. What should the engineer do so the Deploy job receives the value?
jobs:
- job: Build
steps:
- script: echo "##vso[task.setvariable variable=imageTag]$(Build.BuildId)"
- job: Deploy
steps:
- script: echo "Deploying tag $(imageTag)"
- AChange the Deploy job to print $[ variables.imageTag ] and leave the rest of the pipeline unchanged.
- BGive the script step the name setTag, add isOutput=true to the logging command, add dependsOn: Build to the Deploy job, and map the value with imageTag: $[ dependencies.Build.outputs['setTag.imageTag'] ].✓
- CMove imageTag into a variable group that is linked to the pipeline, and keep setting it with task.setvariable in the Build job.
- DReference the value in the Deploy job as $(stageDependencies.Build.setTag.outputs.imageTag).
Correct answer: B — Give the script step the name setTag, add isOutput=true to the logging command, add dependsOn: Build to the Deploy job, and map the value with imageTag: $[ dependencies.Build.outputs['setTag.imageTag'] ].
A variable set with task.setvariable lives only inside the job that set it. Adding isOutput=true turns it into an output variable, and output variables are addressed by step name, which is why the step needs name: setTag. The consuming job must declare dependsOn: Build, because dependencies.<job>.outputs is only populated for jobs you depend on, and the mapping has to use runtime expression syntax $[ ] in a variables block. Option A still reads a variable that was never defined in that job. Option C fails because variable groups are read when the run is queued and task.setvariable never writes back to them. Option D uses stageDependencies, which is for reading jobs in a previous stage, and macro syntax $( ) cannot resolve it anyway.
Define variablesA team deploys a web app to Azure App Service. The release pipeline deploys to a staging slot and then swaps the slot into production. Two problems keep happening. First, after a swap the production app sometimes talks to the staging database, because the connection string moves with the code. Second, for the first minute after a swap some users get slow responses and HTTP 500 errors while the app starts up. Which TWO actions fix these problems? (Select TWO.)
- AMark the database connection string and the other environment-specific app settings as deployment slot settings on both slots.✓
- BEnable auto swap on the staging slot so that the swap runs immediately after every deployment.
- CAdd the WEBSITE_SWAP_WARMUP_PING_PATH and WEBSITE_SWAP_WARMUP_PING_STATUSES app settings so the swap waits until the warmed instances answer on the health endpoint.✓
- DPut Azure Front Door in front of the app and drain traffic from the production origin before each swap.
- EScale the App Service plan out to more instances before each swap and scale back in afterwards.
Correct answer: A, C — Mark the database connection string and the other environment-specific app settings as deployment slot settings on both slots. · Add the WEBSITE_SWAP_WARMUP_PING_PATH and WEBSITE_SWAP_WARMUP_PING_STATUSES app settings so the swap waits until the warmed instances answer on the health endpoint.
By default app settings and connection strings are part of the content that moves during a swap, so the staging value follows the code into production. Marking a setting as a deployment slot setting makes it sticky, so it stays with the slot and production keeps the production connection string. For the second problem, App Service restarts the staging instances with the production settings and pings them before the swap completes; WEBSITE_SWAP_WARMUP_PING_PATH and WEBSITE_SWAP_WARMUP_PING_STATUSES point that ping at a real health endpoint and define which status codes count as ready, so the swap only finishes once the app is actually warm. Option B only automates when the swap happens and fixes neither problem. Options D and E add cost and complexity while the swapped instances still start cold.
Set up staging environments in Azure App ServiceA pipeline deploys to Azure Kubernetes Service with the Kubernetes manifest task using the canary strategy at 20 percent. The team wants to compare error rates before sending all traffic to the new build. Which statement about this strategy is CORRECT?
- AThe baseline variant runs the new image and the canary variant runs the current stable image, so the two can be compared side by side.
- BThe baseline variant runs the image of the current stable workload and the canary variant runs the new image, at the same small replica count; a later job runs the task with action: promote or action: reject.✓
- CThe task raises the canary share automatically until it reaches 100 percent, and the promote and reject actions apply only to the blue-green strategy.
- DThe canary strategy needs a service mesh that supports SMI; without one installed in the cluster the task deploys the manifests as an ordinary rollout.
Correct answer: B — The baseline variant runs the image of the current stable workload and the canary variant runs the new image, at the same small replica count; a later job runs the task with action: promote or action: reject.
Canary deploys two extra workloads: -canary with the new image and -baseline with the image the stable workload is already running, both at the reduced replica count, so the comparison is fair and does not blame normal cold-start noise on the new build. Nothing advances on its own: a later job, usually gated by an environment approval or check, runs the task again with action: promote, which updates the stable workload and deletes both variants, or action: reject, which deletes the variants and leaves stable alone. A reverses the two variants. C invents automatic promotion. D is wrong because SMI traffic splitting is an optional trafficSplitMethod, not a requirement.
Kubernetes deployment strategies in Azure PipelinesA team tracks work as User Story work items in Azure Boards and runs manual tests in Azure Test Plans. When a test fails, the QA lead must be able to open the failed result and see which user story that test verifies, without searching. The team also wants each user story to show its own test pass rate. Which action provides this traceability with the LEAST manual effort?
- ACreate a static test suite for each sprint and put the user story ID at the start of every test case title.
- BCreate a requirement-based test suite from each user story, then add the test cases to that suite.✓
- CCreate a query-based test suite that returns all test cases carrying a tag named after the user story.
- DAdd a hyperlink from each test case work item to the matching user story work item.
Correct answer: B — Create a requirement-based test suite from each user story, then add the test cases to that suite.
A requirement-based test suite is built from a backlog work item, so every test case you add to it is automatically joined to that user story with a Tests / Tested By link. That link is what drives the traceability report and the test status shown on the story, so a failed run points straight back to the requirement. A static suite with IDs in the title is just text, and it breaks the moment someone renames a test. A query-based suite groups test cases by a query but creates no link to any requirement. A hyperlink is a generic link type and is ignored by the requirements traceability views and roll-ups.
Create test plans and test suitesA pipeline uses an Azure Resource Manager service connection named prod-arm in the project Payments, inside the Azure DevOps organization contoso. The connection authenticates with a Microsoft Entra app registration that uses a client secret, and the secret expires every year. The team wants to remove the secret and use workload identity federation instead. An engineer adds a federated credential to the app registration. Which subject identifier must the federated credential use?
- Ahttps://dev.azure.com/contoso/Payments/prod-arm
- Brepo:contoso/payments:ref:refs/heads/main
- Csc://contoso/Payments/prod-arm✓
- DThe object ID of the service connection, in GUID form
Correct answer: C — sc://contoso/Payments/prod-arm
For an Azure Pipelines service connection the federated credential subject follows the pattern sc://<organization>/<project>/<service connection name>, the issuer is https://vstoken.dev.azure.com/<organization ID>, and the audience is api://AzureADTokenExchange. The values must match exactly, which is why renaming the project or the service connection breaks the trust and the pipeline starts failing to sign in. Option B is the subject format used by GitHub Actions, not Azure Pipelines. Option A is only the web URL of the project and is not a valid subject. Option D is not used in a federated credential at all.
Workload identity federationAn application runs on six instances behind a load balancer and stores data in Azure SQL Database. The next release renames the Surname column to LastName. Deployments are rolling, so old and new application code run at the same time for about 10 minutes. The release must cause no downtime and no data loss. What should the team do?
- ADeploy the DACPAC with SqlPackage and set BlockOnPossibleDataLoss to false so the column change is applied in a single step during the rolling deployment.
- BAdd an EF Core migration that calls RenameColumn and run it as the first step of the release, before any new instance starts.
- CSplit the change over three releases: add LastName and have the code write to both columns, then backfill LastName and switch all reads to it, then drop Surname in a later release.✓
- DPut the app into maintenance mode, run the EF Core migration while no instance is serving traffic, then bring the instances back.
Correct answer: C — Split the change over three releases: add LastName and have the code write to both columns, then backfill LastName and switch all reads to it, then drop Surname in a later release.
This is the expand and contract pattern: the schema is widened first so both the old and new versions of the code can run against it, and the old column is only dropped after every instance is running code that no longer reads it. Option A is dangerous because turning off the block on possible data loss lets the deployment drop and recreate the column, and the values in Surname are gone. Option B renames in one step, so the instances still running old code query a column that no longer exists and the rollout produces errors. Option D protects the data but takes the application offline, which breaks the no downtime requirement.
An Azure DevOps organization builds pull requests from a repository owned by a company GitHub organization. The service connection uses a personal access token created by an engineer who is leaving the company. Builds sometimes fail with GitHub API rate limit errors, and the pull request status check appears under that engineer's account. Which change fixes both problems with the LEAST ongoing maintenance?
- ACreate a new personal access token under a shared service account and update the existing service connection.
- BInstall the Azure Pipelines GitHub App on the GitHub organization and switch the pipeline's repository connection to the app.✓
- CRecreate the service connection by using GitHub OAuth and sign in as a GitHub organization owner.
- DCreate a fine-grained personal access token with the Checks and Contents permissions and store it in a variable group.
Correct answer: B — Install the Azure Pipelines GitHub App on the GitHub organization and switch the pipeline's repository connection to the app.
The Azure Pipelines GitHub App authenticates as an installed app, so it has its own API rate limit and posts results through the Checks API under the Azure Pipelines identity instead of a person. There is no token to rotate and nothing breaks when someone leaves. A shared-account token or a fine-grained token still ties the connection to a user account and its rate limit, and someone must rotate it before it expires. An OAuth connection made by an organization owner has the same problem and also posts statuses as that owner.
Build GitHub repositoriesA company wants developers to create their own sandbox application environments in Azure without holding permissions on the subscription. Only infrastructure templates reviewed by the platform team may be used, and every sandbox environment must be removed automatically after seven days. The platform team has created an Azure Deployment Environments dev center and a project. Which TWO actions should the platform team perform next? (Select TWO.)
- AGrant every developer the Contributor role on the sandbox Azure subscription.
- BAttach a catalog to the dev center that points at a Git repository holding the approved environment definitions.✓
- CCreate an Azure Dev Box pool in the dev center and give developers the DevCenter Dev Box User role.
- DCreate project environment types that map to the sandbox subscription, and give developers the Deployment Environments User role.✓
- EWrite an Azure Automation runbook that deletes any resource group older than seven days.
Correct answer: B, D — Attach a catalog to the dev center that points at a Git repository holding the approved environment definitions. · Create project environment types that map to the sandbox subscription, and give developers the Deployment Environments User role.
A catalog is the Git repository of environment definitions, so attaching one is how the platform team controls which templates developers may deploy. Project environment types map the environment to a target subscription and a managed identity that performs the deployment, and the Deployment Environments User role lets developers create environments without any rights of their own on that subscription. Granting Contributor on the subscription throws away the governance the service provides. Dev Box pools deliver developer workstations, not application environments. A cleanup runbook is unnecessary because an environment can carry an expiration date and is deleted automatically when it is reached.
What is Azure Deployment Environments?A nightly release pipeline runs in Azure Pipelines. The team wants the on-call engineer to receive an email within minutes whenever a run of that pipeline fails. Other pipelines in the project must not trigger the email. Which solution meets the requirement with the LEAST operational overhead?
- AStream Azure DevOps audit logs to a Log Analytics workspace, then create an Azure Monitor log search alert rule with an action group that emails the on-call engineer.
- BAdd a final job with condition: failed() that posts to an Azure Logic App webhook, and have the Logic App send the email.
- CCreate a custom notification subscription in the Azure DevOps project that fires when a build fails, filtered to that pipeline, and deliver it to the on-call group.✓
- DCreate an Azure Monitor activity log alert on the Azure DevOps organization and attach an action group that emails the on-call engineer.
Correct answer: C — Create a custom notification subscription in the Azure DevOps project that fires when a build fails, filtered to that pipeline, and deliver it to the on-call group.
Azure DevOps has built-in notification subscriptions for pipeline events, and a custom subscription can be filtered by pipeline and by outcome, then delivered to a team group or distribution list. Nothing has to be built or paid for. Audit logs record administrative activity such as permission changes, not the pass or fail result of a run, so a log search alert on them never fires. A failure job calling a Logic App does work, but it adds a workflow, a webhook secret and pipeline code to maintain, and it is skipped if the run is cancelled or the agent never starts. Azure DevOps organizations are not Azure resources that write pipeline results to the Azure activity log, so option D has no data to alert on.
About notificationsProject settings keep pipeline runs for 30 days. A storage report still shows hundreds of runs from more than a year ago, all belonging to one pipeline, and their artifacts are still there too. What should the team do to let these runs be cleaned up?
- ARaise the Minimum runs to keep value so the retention job has room to work.
- BDelete the retention leases on those runs, because a run held by a lease, such as one consumed by a release or marked to be retained, is never removed by the policy.✓
- CNothing can be done: project retention deletes artifacts and logs, and the run record itself always stays.
- DDelete the pipeline and create it again, because retention settings apply only to runs created after the policy was set.
Correct answer: B — Delete the retention leases on those runs, because a run held by a lease, such as one consumed by a release or marked to be retained, is never removed by the policy.
Retention policies skip any run that has an active retention lease. Leases come from a release that consumed the run, from a pipeline that called the retain-run behaviour for an environment deployment, or from someone choosing Retain in the run menu; once the lease is deleted the next retention pass removes the run and its artifacts. A does the opposite and keeps even more runs. C is wrong because the policy does delete run records once no lease blocks it. D destroys history for no reason, since retention applies to existing runs as well.
Set retention policies for builds, releases, and testsAn engineer opens the Logs blade of an Application Insights resource and wants a chart of the PERCENTAGE of failed requests for each hour over the last 24 hours. Which query produces that chart?
- Arequests | where timestamp > ago(24h) | where success == false | summarize count() by bin(timestamp, 1h) | render timechart
- Brequests | where timestamp > ago(24h) | summarize failed = countif(success == false), total = count() by timestamp | extend failRate = failed / total | render timechart
- Crequests | where timestamp > ago(24h) | summarize failed = countif(success == false), total = count() by bin(timestamp, 1h) | extend failRate = 100.0 * failed / total | project timestamp, failRate | render timechart✓
- Drequests | where timestamp > ago(24h) | summarize total = count() by bin(timestamp, 1h) | where success == false | render timechart
Correct answer: C — requests | where timestamp > ago(24h) | summarize failed = countif(success == false), total = count() by bin(timestamp, 1h) | extend failRate = 100.0 * failed / total | project timestamp, failRate | render timechart
bin(timestamp, 1h) groups the rows into hourly buckets, countif counts only the failed requests inside each bucket, count() gives the bucket total, and multiplying by 100.0 forces floating point division so the rate is a real percentage. A charts the number of failures per hour, not the rate, so a busy hour always looks worse. B groups by the raw timestamp, which makes one bucket per millisecond, and dividing two long values truncates the result to 0. D is invalid after the summarize, because the success column no longer exists in the result set.
Log queries in Azure MonitorA YAML pipeline deploys to an Azure Pipelines environment named production. Two pull requests were merged a few minutes apart, and both runs reached the deploy stage at the same time. The overlapping deployments left the site in a broken state. Which environment check must the team add so a second run cannot deploy while another run is still deploying?
- AApprovals
- BBusiness hours
- CExclusive lock✓
- DBranch control
Correct answer: C — Exclusive lock
The exclusive lock check allows only one run at a time to use the protected environment; other runs wait, and by default only the latest of the waiting runs proceeds when the lock is released. Approvals only pause a run until a person clicks approve, so two approvers could still release both runs at once. Business hours delays a run until an allowed time window, which does not stop two runs inside that window. Branch control restricts which branch may deploy, but both merged runs come from the same branch.
Define approvals and checksBuilds must reach a package server and a SQL database that have private endpoints inside an Azure virtual network. Build traffic is bursty: about two hours of work on weekday mornings and almost nothing at night. The team wants agents that reach the private network while keeping idle cost as low as possible. Which option is the MOST cost-effective?
- AUse Microsoft-hosted agents and give the package server and database public endpoints restricted by the weekly hosted agent IP range list.
- BRun self-hosted agents on three virtual machines inside the virtual network and leave them running so builds never wait.
- CCreate an Azure virtual machine scale set agent pool whose scale set sits in a subnet of the virtual network, and set the number of agents to keep on standby to 0.✓
- DRun the build as a container job on Microsoft-hosted agents so the container can reach the private endpoints.
Correct answer: C — Create an Azure virtual machine scale set agent pool whose scale set sits in a subnet of the virtual network, and set the number of agents to keep on standby to 0.
Scale set agents are self-hosted agents that Azure Pipelines scales for you: the scale set lives in your subnet so builds resolve private endpoints, and with standby set to 0 the pool scales down to nothing overnight, so you pay only while builds run. A exposes private data to the internet and forces you to track a weekly IP file. B reaches the network but you pay for three virtual machines around the clock, including the quiet hours. D is wrong because a container job still runs on a Microsoft-hosted machine outside your virtual network.
Azure virtual machine scale set agentsAn open-source project keeps its code in a public GitHub repository. Pull requests from forks are validated by an Azure Pipelines pipeline and by a GitHub Actions workflow. The maintainers must make sure that code contributed in a fork cannot read the project's deployment credentials. Which approach is MOST secure?
- ATurn on "Make secrets available to builds of forks" and rely on the agent masking secret values in the log output.
- BChange the workflow to the pull_request_target event and check out the pull request head so the job can use repository secrets.
- CMove the credentials into Azure Key Vault and read them during the fork build through a variable group linked to the vault.
- DLeave "Make secrets available to builds of forks" turned off in Azure Pipelines, and keep the contributor build on the pull_request event in GitHub Actions.✓
Correct answer: D — Leave "Make secrets available to builds of forks" turned off in Azure Pipelines, and keep the contributor build on the pull_request event in GitHub Actions.
Fork validation must run without secrets. Azure Pipelines does not pass secrets to fork builds unless you enable that trigger setting, and the GitHub Actions pull_request event gives a fork a read-only GITHUB_TOKEN and no repository secrets, so untrusted code has nothing to steal. Option A is unsafe because masking only hides exact strings; contributed code can base64 or split a secret and print it. Option B is the classic pull_request_target trap: the job runs in the base repository context with secrets and a write token, and checking out the pull request head then executes attacker code with them. Option C changes only where the secret is stored; the fork build still ends up holding the value.
Build GitHub repositoriesA product team keeps its service documentation in Markdown. The team wants every documentation change to travel on the same branch as the code change and to be reviewed in the same pull request. The pages also contain Mermaid architecture diagrams that must render for readers. Which approach meets the requirement with the LEAST operational overhead?
- AProvision a project wiki and let writers edit the pages directly in the browser.
- BPublish the /docs folder of the product repository as a wiki from the main branch, so page edits ride in the same pull requests as the code.✓
- CProvision a project wiki and add a pipeline step that copies Markdown files from the product repository into the wiki repository after every merge.
- DKeep the Markdown in the repository only and rely on the file preview in Azure Repos, because wikis cannot render Mermaid diagrams.
Correct answer: B — Publish the /docs folder of the product repository as a wiki from the main branch, so page edits ride in the same pull requests as the code.
Publish code as wiki maps a folder and a branch of a normal Git repository to a wiki, so the pages are branched, reviewed in pull requests and versioned exactly like the code, and you can publish several branches as separate versions. A provisioned wiki is backed by its own hidden repository and browser editing commits straight to it, which skips pull request review. C reaches the same place but adds a sync job that can drift and fail. D is simply wrong: both wiki types render Mermaid inside a fenced ::: mermaid block, so Mermaid support is not what decides this.
Publish a Git repository to a wikiA team deploys a Bicep template to a resource group named rg-payments. The template creates an App Service plan, a web app, and a storage account. The same resource group also contains a Log Analytics workspace and a key vault that a different team created by hand and that are not declared in the template. Every deployment must leave those two resources untouched and every deployment must keep succeeding. Which deployment setting should the team use?
- AUse Complete mode so the resource group always matches the template exactly.
- BUse Incremental mode.✓
- CUse Complete mode and run a what-if operation before each deployment.
- DUse Complete mode and add a CanNotDelete lock to the workspace and the key vault.
Correct answer: B — Use Incremental mode.
Incremental mode, which is the default, adds or updates the resources declared in the template and ignores anything else that already exists in the resource group, so the shared workspace and key vault survive. Complete mode deletes every resource in the resource group that is not in the template, so option A destroys the other team's resources. Option C only previews that same destructive change; what-if is a preview tool, not a deployment mode, and the delete still happens when you deploy. Option D does stop the deletion, but a Complete mode deployment that tries to delete a locked resource fails, so deployments would stop succeeding.
Azure Resource Manager deployment modesA build takes six minutes to restore npm packages on every run. An engineer adds the steps below to speed it up. Which statement about this pipeline is TRUE?
steps:
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_config_cache)
- script: npm ci
- AIf no cache matches the key, the Cache task fails and the whole job must be re-run.
- BThe cache saved under the same key is overwritten at the end of every run, so the cache always contains the newest packages.
- CIf the restore key matches several caches, the task restores all of them and merges the contents into the path.
- DIf the key does not match but the restore key does, an older cache is restored, npm ci still runs, and a new cache is uploaded under the exact key when the job succeeds.✓
Correct answer: D — If the key does not match but the restore key does, an older cache is restored, npm ci still runs, and a new cache is uploaded under the exact key when the job succeeds.
A cache miss is never an error. The task logs the miss, the build carries on, and a post-job step uploads the cache under the exact key only if the job succeeds. Restore keys give you a partial hit: the newest cache whose key starts with the restore key prefix is restored, and because the exact key was not matched the task still saves a fresh entry at the end of the run. Option A is wrong because a miss does not fail the job. Option B is wrong because cache entries are immutable - once a key exists it is never overwritten, which is why the lock file hash belongs in the key. Option C is wrong because only one cache is ever restored, not merged.
Pipeline cachingA company runs one Azure Kubernetes Service cluster. The team needs container stdout and stderr logs plus node and pod performance metrics in a Log Analytics workspace. Logs from the dev and test namespaces are very noisy and nobody reads them. The team wants the LOWEST ingestion cost. What should the team do?
- AEnable Container insights on the cluster and edit its data collection rule to exclude the dev and test namespaces from stdout and stderr collection and to raise the collection interval.✓
- BEnable VM insights on the cluster nodes and use the Dependency agent to collect the container logs.
- CInstall the Log Analytics agent on each node and give it a custom data collection rule that filters the namespaces.
- DEnable Container insights with the default settings, then run a nightly purge job on the ContainerLogV2 table to delete rows from the dev and test namespaces.
Correct answer: A — Enable Container insights on the cluster and edit its data collection rule to exclude the dev and test namespaces from stdout and stderr collection and to raise the collection interval.
Container insights deploys the Azure Monitor Agent for containers and is driven by a data collection rule. That rule holds the cost settings, including which namespaces stdout and stderr are collected from and how often metrics are sampled, so the noisy data is filtered at the source and never billed. VM insights in option B is for virtual machines: it gives guest performance counters and a dependency map, not container logs from pods. The Log Analytics agent in option C was retired in August 2024 and never used data collection rules. Option D still ingests everything first, so the cost is already paid, and purge is meant for privacy requests rather than routine cleanup.
Container insights overviewA platform team owns a YAML template in a separate Azure Repos repository. The template runs mandatory scanning and signing steps. Every product pipeline must build through that template, and product teams keep write access to their own pipeline YAML files and can run pipelines from any branch. Deployments use one service connection to the production subscription. Which approach stops a product team from running a pipeline that skips the template?
- APublish the mandatory steps as a task group and ask every product team to add the task group to their pipeline.
- BInclude the template inside the steps list of every product pipeline and add a branch policy that requires a platform team reviewer on the pipeline file.
- CHave each product pipeline use extends with the template from a repository resource, and add a Required template check to the production service connection that names that repository and template.✓
- DConvert the template into a pipeline decorator so the steps are injected into every job, and give the platform team Contributor access on each product repository.
Correct answer: C — Have each product pipeline use extends with the template from a repository resource, and add a Required template check to the production service connection that names that repository and template.
The Required template check is evaluated on the protected resource. When a pipeline asks to use the production service connection, the check confirms that the run extends from the named template in the named repository, and the run fails before the resource is used if it does not. Option A does not enforce anything, and task groups are a classic feature that YAML pipelines cannot call at all. Option B can be edited out of the file, and a branch policy only guards the protected branch, so the team can still run the pipeline from a topic branch. Option D injects steps into every job, but a decorator never checks that the build went through the governed template, and handing out Contributor rights enforces nothing.
Define approvals and checksReady to try it under exam conditions?
Reading answers is not the same as recalling them with a clock running. Take the same 50 questions as a timed mock exam — 100 minutes, no feedback until you submit, then a score broken down by exam domain so you know what to study.
Start the timed AZ-400 test →