Security and compliance plan

AZ-400 notes, 10–15% of the exam, updated November 2026

Security is 10-15% of AZ-400. The recurring theme: no long-lived secrets in pipelines. Questions ask you to pick the identity type, the place to keep a secret, the right access level for a person, or the scanner that finds a given class of problem. Prefer managed identities and OIDC federation, least privilege, and scanners that run automatically.

Identities for automation

Service principal vs managed identity

Service principal (app registration) Managed identity
What An identity for an application in Microsoft Entra ID An identity Azure manages for an Azure resource
Credentials Client secret or certificate (you store, rotate), or a federated credential (none to store) None to manage; Azure rotates automatically
Works from Anywhere: pipelines, GitHub, on-premises, other clouds Only code running on an Azure resource that supports it (VM, App Service, Functions, AKS, Container Apps)
Lifecycle You create and delete it Tied to the resource (system-assigned) or independent (user-assigned)

System-assigned managed identity: created with the resource, one per resource, deleted with it. User-assigned: created as its own Azure resource, can be attached to many resources, survives deleting any one of them, and can be pre-authorised before resources are deployed. Choose user-assigned when several resources share the same access, or when you need the identity ready before the resource exists, or a stable identity across redeployments; choose system-assigned for a simple one-to-one case.

Rules of thumb:

  • Code running in Azure that needs another Azure service -> managed identity, access through DefaultAzureCredential, no secret anywhere.
  • A pipeline or workflow running outside Azure (GitHub-hosted runner, Microsoft-hosted agent) -> service principal with workload identity federation, or a managed identity federated credential. A self-hosted agent on an Azure VM can use that VM’s managed identity.
  • Grant Azure RBAC roles at the narrowest scope (resource group, resource) and the least privileged role (Contributor, Reader, or a specific data role such as Key Vault Secrets User).
  • A service principal secret expires (default up to 2 years) and a leaked one is a breach, so prefer certificates or federation.

GitHub authentication

Mechanism Description and use
GITHUB_TOKEN An installation token generated automatically for each workflow run. Expires when the job ends. Scoped to the repository. Limited with the permissions: key (contents: read, pull-requests: write, packages: write, id-token: write). Use for most in-repo tasks. Actions it triggers do not start new workflow runs (prevents loops)
GitHub App An app with its own fine-grained permissions installed on an organisation or repos. Acts as itself (a bot), or on behalf of a user. Generates short-lived installation tokens (1 hour). Higher rate limits, not tied to a person. Best for cross-repository automation and integrations. Use actions/create-github-app-token in a workflow
Fine-grained personal access token (PAT) Tied to a person, repo-scoped, specific permissions, expiry required. Use for scripts when no app fits
Personal access token (classic) Broad scopes (repo, workflow…), tied to a user, may not expire. Avoid, especially for automation (breaks when the person leaves)
Deploy key SSH key giving a single repository read (or write) access
OAuth app Third-party apps acting for users
SSH keys / GPG Developer Git authentication and commit signing

Choose: workflow touching its own repo -> GITHUB_TOKEN with minimal permissions; automation across repos or needing to trigger further workflows -> GitHub App (a PAT or GITHUB_TOKEN pushes don’t trigger workflows the same way); a person’s script -> fine-grained PAT. For SAML SSO organisations, PATs must be authorised for the organisation.

Azure DevOps service connections and PATs

A service connection stores how a pipeline authenticates to an external service (Azure, GitHub, Docker registry, Kubernetes, SSH, npm…).

For Azure Resource Manager the authentication choices are:

Option Notes
Workload identity federation (OIDC) Recommended. No secret, short-lived tokens. App registration or managed identity with a federated credential. Can be created automatically or manually
Managed identity For self-hosted agents on Azure resources
Service principal (secret or certificate) Legacy. Must rotate the secret, and expiry breaks pipelines
Publish profile Per-app deployment credentials, avoid

Security for service connections:

  • Scope to a subscription or resource group, not broader.
  • Don’t grant “to all pipelines” access. Use pipeline permissions to authorise specific pipelines, and add checks and approvals on the service connection (approval, branch control) so only main can use the production connection.
  • Use separate connections per environment (dev/prod).
  • Roles on a connection: Reader, User, Creator, Administrator.

Personal access tokens (Azure DevOps) authenticate scripts and tools as a user. Limit scopes, set short expiry (max 1 year), store as secrets, revoke when no longer needed. Admins can restrict PAT creation through policy, and organisations can enable Microsoft Entra-based tokens instead. Prefer service principals or managed identities added to the organisation over PATs for automation, as they aren’t tied to a person.

System.AccessToken (the job access token) is the build service identity’s short-lived token. Expose it explicitly to scripts through env: SYSTEM_ACCESSTOKEN: $(System.AccessToken); permissions come from the project’s Build Service account.

Permissions in GitHub

  • Organisation roles: Owner, Member, Moderator, Billing manager, Security manager, GitHub App manager. Repository roles: Read, Triage, Write, Maintain, Admin (and custom roles).
  • Use teams (nested teams inherit) for access. Grant the least role, and use base permissions for organisation members.
  • Outside collaborators access specific repos without being members. Review them regularly, and require 2FA at organisation level.
  • Enterprise controls: policies, SAML SSO, Enterprise Managed Users (identities from Entra ID), audit log streaming.
  • Secrets and variables are scoped to organisation, repository or environment. Environments add required reviewers.
  • Actions permissions: restrict allowed actions (GitHub-authored only, verified creators, or an allow list pinned by SHA) and set the default GITHUB_TOKEN to read-only.

Permissions in Azure DevOps

  • Security groups at organisation (Project Collection Administrators, Project Collection Valid Users), project (Project Administrators, Contributors, Readers, Build Administrators, Release Administrators, Project Valid Users) and team level. Use Microsoft Entra groups to manage membership centrally.
  • Permissions are Allow, Deny, Not set, Inherited. Deny beats Allow, except explicit Allow at a lower level overriding an inherited Deny is possible, and membership in the Project Collection Administrators group bypasses most.
  • Object-level security applies on repos, branches, pipelines, environments, feeds, area paths, iteration paths, queries, dashboards and service connections.
  • Pipeline security: restrict who can edit and queue a pipeline, and protect resources (secure files, variable groups, agent pools, environments) so a pipeline must be authorised to use them.
  • Principle: put people in groups, then grant groups the permission.

Access levels (licences)

Level Who Capability
Basic Developers and contributors Full access to Repos, Boards, Pipelines, Artifacts (first 5 users free)
Basic + Test Plans Testers Adds Test Plans
Visual Studio subscriber Users with VS subscription Matches their subscription entitlements
Stakeholder Free, unlimited Limited: view and add work items, use Boards (limited), view dashboards, approve pipeline checks; no access to Repos code, can’t use Test Plans. Private projects have stakeholder restrictions; public projects give stakeholders more
GitHub Advanced Security (add-on) Active committers Licensing per active committer for GitHub Advanced Security for Azure DevOps

The exam trick: a product owner or executive who only needs to view boards and dashboards gets Stakeholder (free). A tester needing Test Plans needs the Basic + Test Plans level. GitHub equivalent: outside collaborator for external parties, seat licences for members, and Read role for view-only users.

Projects and teams in Azure DevOps

  • An organisation holds projects. A project holds repos, pipelines, boards, artifacts, wiki, and test plans. Use one project for related teams sharing work (simpler cross-team reporting and shared pipelines), separate projects when you need strong isolation (permissions, process, regulated data).
  • A team is a subset of project members with their own backlog, board, sprints, dashboard and notifications, scoped by area paths and iteration paths. Each project gets a default team.
  • Configure: create a team, add members (from groups), set area path ownership, select iterations, set working days and capacity, configure board columns.
  • Choose the process (Basic, Agile, Scrum, CMMI) per project. Project visibility: private or public.

Managing sensitive information

Azure Key Vault

Key Vault stores secrets (strings), keys (cryptographic keys, software or HSM-backed) and certificates (with lifecycle and auto-renewal with supported CAs). Know:

  • Authorisation: Azure RBAC (recommended; roles Key Vault Secrets User, Secrets Officer, Crypto User, Certificates Officer, Administrator) or the older access policies. Pipelines and apps use a managed identity or federated service principal with Secrets User.
  • Protection: soft delete (on by default, retained 7-90 days) and purge protection (must wait out the retention period before purge), private endpoints, firewall rules, and diagnostic logs to Log Analytics.
  • Versioning and rotation: secrets have versions; reference without a version to get the latest. Event Grid events (near expiry, new version) trigger rotation. Certificates can auto-renew.
  • Tiers: Standard (software keys) and Premium (HSM-protected). Managed HSM is a dedicated single-tenant HSM pool.
  • Access from a pipeline:
    • Azure Key Vault task (AzureKeyVault@2) downloads secrets as pipeline variables (masked) for the run, using a service connection.
    • Variable group linked to Key Vault exposes secrets as variables by name, fetched at run time; the service connection needs get and list on secrets.
    • App Service / Functions Key Vault references (@Microsoft.KeyVault(SecretUri=...)) so the app reads the secret through its managed identity.
    • In GitHub Actions: azure/login with OIDC then azure/get-keyvault-secrets (or az keyvault secret show), and the value must be masked (::add-mask::).
  • Don’t copy Key Vault secrets into repository secrets unless needed, because you then have two places to rotate.

Secretless authentication: workload identity federation and OIDC

The pipeline requests a short-lived OIDC token from its platform (GitHub or Azure DevOps). Microsoft Entra ID trusts that token because a federated credential on the app registration (or user-assigned managed identity) names the issuer and subject. Entra exchanges it for an access token. No client secret exists to leak or rotate.

GitHub Actions:

permissions:
  id-token: write      # allow requesting the OIDC token
  contents: read
steps:
  - uses: azure/login@v2
    with:
      client-id: ${{ vars.AZURE_CLIENT_ID }}
      tenant-id: ${{ vars.AZURE_TENANT_ID }}
      subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
  • The federated credential’s subject pins who can get a token, such as repo:org/repo:ref:refs/heads/main, repo:org/repo:environment:production, repo:org/repo:pull_request, or a tag. A narrow subject limits abuse; the issuer is https://token.actions.githubusercontent.com.
  • Client ID, tenant ID and subscription ID aren’t secrets, so use variables, not secrets.
  • The id-token: write permission is required. Forgetting it is a classic failure.

Azure Pipelines: create an Azure Resource Manager service connection with workload identity federation (automatic creates the app registration and federated credential with issuer https://vstoken.dev.azure.com/<org-id> and subject like sc://org/project/connection). Tasks use the service connection as usual, with no secret.

OIDC also works for AWS and GCP federation, and for Terraform (use_oidc). Benefits: no secret rotation, short token lifetime, conditions on branch/environment, and least privilege RBAC on the identity.

Other secret-handling patterns:

  • GitHub Actions secrets: encrypted, masked in logs, not passed to workflows from forks (except pull_request_target, a risk). Organisation, repository and environment scope. Secrets are available only to jobs that reference them.
  • Azure Pipelines secret variables: masked, encrypted, and not automatically passed to scripts. Map them: env: MY_SECRET: $(mySecret).
  • Variable groups with security roles (Reader, User, Administrator) and optional approvals/checks on the group.

Secure files

Secure files (Pipelines > Library) are encrypted binary files such as certificates (.pfx), provisioning profiles, SSH keys and keystores. A pipeline uses them via DownloadSecureFile@1, which copies the file to the agent, exposes its path as secureFile.secureFilePath, and deletes it after the job. Pipelines must be authorised to use the file, and checks (approvals) can be set on a secure file. Max size 10 MB. Prefer Key Vault certificates for rotation and auditing; secure files fit mobile signing certificates and similar. GitHub has no secure-files object: store a base64-encoded file as a secret (size limit 48 KB) or fetch it from Key Vault, and decode it in the job.

Preventing secret leakage in pipelines

  • Never hardcode secrets in YAML, scripts or repo files. Use Key Vault, secrets and federation.
  • Secrets are masked in logs, but only by exact value match. A transformed value (base64, substring) isn’t masked, so don’t print secrets or derive and echo them. Avoid set -x/Debug logging with secrets. Use ::add-mask:: for runtime values and ##vso[task.setvariable variable=x;issecret=true] in Azure Pipelines.
  • Don’t pass secrets on the command line: process lists and logs show arguments. Use environment variables or stdin.
  • Fork pull requests: don’t expose secrets to fork builds. Azure Pipelines doesn’t by default for forks of GitHub repos. In GitHub, pull_request_target runs with secrets, so don’t check out and run untrusted code in it.
  • Script injection: never interpolate untrusted input (issue titles, branch names, PR bodies) straight into run: with ${{ github.event... }}. Pass it through env: and quote it. Same in Azure Pipelines with $(Build.SourceBranchName)-style values in inline scripts.
  • Least privilege GITHUB_TOKEN, short-lived credentials, separate identities per environment.
  • Pin third-party actions and tasks to a commit SHA or exact version. Restrict allowed actions. Review marketplace extensions.
  • Use approvals and branch control on production connections and environments, so a feature branch can’t deploy to production or read production secrets.
  • Scan for secrets (secret scanning, push protection, gitleaks, CredScan) and rotate anything exposed immediately.
  • Self-hosted agents: use ephemeral runners to avoid secrets lingering on disk, and don’t share them between untrusted repos (especially public ones).
  • Artifacts and logs can hold secrets, so control access and retention.
  • Audit: Azure DevOps auditing streams to Log Analytics/Event Hubs/Sentinel; GitHub audit log and log streaming.

Security and compliance scanning

Strategy: scanning types

Scan What it finds Tools
SCA / dependency Known vulnerabilities in open-source libraries Dependabot, GitHub dependency review, Defender for Cloud, Mend, Snyk, OWASP Dependency-Check
SAST (code) Flaws in your source code (injection, unsafe functions) CodeQL (code scanning), SonarQube/SonarCloud, Microsoft Security DevOps (MSDO) analysers
Secret scanning Credentials committed in code GitHub secret scanning and push protection, GitHub Advanced Security for Azure DevOps, CredScan, gitleaks
Licence compliance Dependencies with disallowed licences (GPL in proprietary product) Dependency review action (deny-licenses/allow-licenses), Mend, FOSSA, Black Duck, ClearlyDefined/SBOM tools
Container/image Vulnerabilities in OS packages and layers Defender for Containers, Trivy, Grype, Microsoft Defender for Cloud scanning ACR images
IaC scanning Misconfigured Bicep/ARM/Terraform/Kubernetes manifests Defender for Cloud DevOps security with MSDO (Template Analyzer, Checkov, Terrascan), PSRule for Azure
DAST Vulnerabilities in the running app OWASP ZAP in a test stage

Strategy: shift left (IDE and pre-commit, then PR), then gate merges and releases on results, plus continuous scanning on a schedule because new CVEs appear against old code. Fail builds above a severity threshold, track findings to resolution and report centrally.

GitHub Advanced Security (GHAS)

On GitHub, GHAS features (free for public repos, paid for private on Enterprise plans; GitHub Secret Protection and GitHub Code Security are the newer standalone products) include:

  • Code scanning with CodeQL (and third-party SARIF tools). Results appear in the Security tab and on pull requests as annotations. Copilot Autofix suggests fixes. Configure through default setup (one click, auto-detected languages, build mode) or advanced setup (a workflow file codeql.yml).
  • Secret scanning (partner patterns, custom patterns, validity checks) and push protection which blocks a push that contains a detected secret unless bypassed with a reason. Also scans issues and PRs.
  • Dependency review: a PR diff of dependency changes with vulnerability and licence data, enforceable through the actions/dependency-review-action.
  • Dependabot, security overview, security advisories, and security campaigns.

GitHub Advanced Security for Azure DevOps brings the same capabilities to Azure Repos: secret scanning, push protection, dependency scanning and code scanning (CodeQL), enabled per repository, project or organisation, billed per active committer. Add the pipeline tasks AdvancedSecurity-Codeql-Init@1, AdvancedSecurity-Codeql-Autobuild@1, AdvancedSecurity-Codeql-Analyze@1 and AdvancedSecurity-Dependency-Scanning@1 to a pipeline to run the analyses. Alerts appear under Repos > Advanced Security.

CodeQL in a workflow and in containers

name: codeql
on:
  push: { branches: [main] }
  pull_request: { branches: [main] }
  schedule: [{ cron: '30 2 * * 1' }]
jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      contents: read
    strategy:
      matrix:
        language: [javascript-typescript, csharp]
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with: { languages: ${{ matrix.language }} }
      - uses: github/codeql-action/autobuild@v3
      - uses: github/codeql-action/analyze@v3
  • security-events: write lets the job upload results.
  • Compiled languages (C#, Java, C/C++, Go…) need a build between init and analyze (autobuild or manual commands); interpreted ones (JavaScript, Python) don’t.
  • CodeQL in a container: when the job’s build runs in a container (container: image) or inside a Docker build, run the CodeQL steps inside that container (the job container: setting makes all steps run in it), or build via the container within the analysed step so CodeQL’s tracer observes the compilation. CodeQL analyses source code, not an image’s installed packages, so for image contents use an image vulnerability scanner.
  • Run CodeQL on pull_request and push for gating, and on a schedule for continuous coverage.

Container scanning

  • Defender for Containers scans images in Azure Container Registry (on push, on pull, and continuously for recently pulled images), reports vulnerabilities through Defender for Cloud, and covers AKS runtime.
  • In the pipeline: run Trivy or Grype between build and push (fail on HIGH,CRITICAL), or the MSDO container image scan. Example:
- script: trivy image --exit-code 1 --severity HIGH,CRITICAL myacr.azurecr.io/app:$(Build.BuildId)
  • Use minimal base images (distroless, alpine), pin by digest, rebuild regularly, scan Dockerfiles and Kubernetes manifests, and sign images (Notation or cosign) with an admission policy that only allows signed, scanned images (Azure Policy for Kubernetes).
  • Registries: use ACR with private endpoints, disable admin user, and authenticate with managed identities or OIDC.

Dependabot

  • Dependabot alerts: notify when a dependency in the dependency graph has a known advisory (GitHub Advisory Database). Enable at repo or organisation level. Reads manifests and lock files in supported ecosystems.
  • Dependabot security updates: automatically open a PR that bumps the vulnerable dependency to the fixed version.
  • Dependabot version updates: scheduled PRs that keep dependencies current, configured in .github/dependabot.yml (ecosystem, directory, schedule, grouping, registries for private feeds such as Azure Artifacts, ignore rules).
  • Dependency review blocks adding vulnerable dependencies in a PR.
  • Auto-triage rules and auto-merge workflows reduce noise. Alerts feed security overview and Defender for Cloud.
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule: { interval: weekly }
    open-pull-requests-limit: 5

Microsoft Defender for Cloud DevOps security

Defender for Cloud DevOps security (part of Defender CSPM) gives a central view of security posture across code and pipelines for Azure DevOps, GitHub and GitLab.

  • Connect the environment: add a DevOps connector to Azure DevOps or GitHub (an Azure DevOps organisation is authorised with Entra ID; GitHub through the Defender for Cloud GitHub App). Select the projects/repos to onboard.
  • It surfaces code, secrets, dependencies, IaC and container findings, and assesses DevOps posture (misconfigurations in repos, pipelines, service connections such as overly permissive settings).
  • Microsoft Security DevOps (MSDO): a command-line tool and an extension/Action (microsoft/security-devops-action, Azure DevOps task MicrosoftSecurityDevOps@1) that runs analysers (Credscan, Bandit, ESLint, Template Analyzer, Terrascan, Trivy, Checkov) in the pipeline and outputs SARIF. Results go to the Defender for Cloud DevOps security page and, in GitHub, to code scanning alerts.
  • Code-to-cloud mapping links a vulnerable code finding to the deployed cloud resource, so you can prioritise by exposure.
  • Pull request annotations on IaC misconfigurations in Azure DevOps and GitHub (for Azure DevOps through the Defender extension; requires Defender CSPM for some features).
  • Recommendations and secure score; trigger workflow automation or Logic Apps to open tickets.

Integrating GHAS with Defender for Cloud

Connect the GitHub organisation to Defender for Cloud through the GitHub connector (install the Defender for Cloud GitHub App with permissions to read repositories and security data). With GitHub Advanced Security enabled:

  • Code scanning, secret scanning and Dependabot alerts flow into Defender for Cloud’s DevOps security dashboard and recommendations.
  • You can see them alongside cloud resources through code-to-cloud context and use attack path analysis.
  • Defender CSPM plan is required for the advanced features.
  • Alerts remain managed in GitHub (dismiss/fix there), and Defender for Cloud provides the central, multi-pipeline view.

Exam checklist

  • You can choose between service principal, system-assigned and user-assigned managed identity, and say why a user-assigned identity can be shared.
  • You know GITHUB_TOKEN, GitHub Apps and PATs, and when to use each, and that permissions: scopes the token.
  • You know Azure DevOps service connection options and that workload identity federation is preferred over secrets, plus how to restrict connections with pipeline permissions and checks.
  • You can set up OIDC in GitHub Actions (id-token: write, azure/login, federated credential with subject) and in Azure Pipelines.
  • You know Key Vault authorisation (RBAC), soft delete, purge protection, the Key Vault task, linked variable groups and Key Vault references.
  • You can pick Stakeholder, Basic, Basic + Test Plans, and GitHub outside collaborator for the described user.
  • You know Azure Pipelines secure files (DownloadSecureFile@1) and how to avoid log leaks, injection and fork exposure.
  • You can match each scan type to a tool: Dependabot (dependencies), CodeQL (code), secret scanning and push protection, dependency review (licences), Defender for Containers or Trivy (images), MSDO (IaC and mixed).
  • You know GHAS for Azure DevOps features and tasks, the Defender for Cloud DevOps connectors, and the role of dependabot.yml.

Test what you just read. 40 practice questions on develop a security and compliance plan.

Practise this domain

All AZ-400 notes