WIKI
AZ-400 DevOps Engineer

07 · Élaborer un plan de sécurité et de conformité

37 min de lecture Mis à jour le 21 octobre 2026
Sur cette page

Version : 1.1 · Date : 2026-10-22 · Domaine 4 de l'examen : Develop a security and compliance plan (10 à 15 %)

En une phrase : une chaîne DevOps sûre donne à chaque pipeline une identité sans secret (managed identity, workload identity federation), applique le moindre privilège aux personnes (access levels, groupes, rôles de dépôt) comme aux pipelines (protected resources, permissions du GITHUB_TOKEN), range les secrets restants dans Key Vault sans jamais les afficher, et analyse chaque push, PR, build et image (GHAS, GHAzDO, Dependabot, Defender for Cloud DevOps security).

Les trois piliers : identités, secrets, analyses

Ce que l'examen demande#

Repris mot pour mot du guide d'étude AZ-400 (compétences mesurées au 27 juillet 2026) :

# Compétence mesurée (guide officiel, en anglais) Section
1 Choose between Microsoft Entra service principals and managed identities for Azure resources (system-assigned and user-assigned) 2
2 Implement and manage GitHub authentication, including GitHub Apps, GITHUB_TOKEN, and personal access tokens 3
3 Implement and manage Azure DevOps service connections and personal access tokens 4
4 Design and implement permissions and roles in GitHub 5
5 Design and implement permissions and security groups in Azure DevOps 6
6 Recommend appropriate access levels, including stakeholder access in Azure DevOps and outside collaborator access in GitHub 7
7 Configure projects and teams in Azure DevOps 8
8 Implement and manage secrets, keys, and certificates by using Azure Key Vault 9
9 Implement and manage secrets and secretless authentication (for example, workload identity federation/OpenID Connect) in GitHub Actions and Azure Pipelines 10
10 Design and implement a strategy for managing sensitive files during deployment, including Azure Pipelines secure files 11
11 Design pipelines to prevent leakage of sensitive information 12
12 Design a strategy for security and compliance scanning, including dependency, code, secret, and licensing scanning 13
13 Configure Microsoft Defender for Cloud DevOps Security 14
14 Configure GitHub Advanced Security for GitHub and GitHub Advanced Security for Azure DevOps 15
15 Integrate GitHub Advanced Security with Microsoft Defender for Cloud 16
16 Automate container scanning, including scanning container images and configuring an action to run CodeQL analysis in a container 17
17 Automate analysis of vulnerabilities of open-source components by using Dependabot alerts 18

Les bases d'Entra ID (managed identities, workload identity federation) et de Key Vault sont détaillées dans SC-500 : managed identities, workload identity federation, rôles Key Vault et Defender for Cloud DevOps security. Ce chapitre reste côté DevOps : pipelines, dépôts, organisations.


1. Les bases à maîtriser#

Terme Ce que c'est
Service principal L'identité d'une app registration dans un tenant Microsoft Entra ; s'authentifie par secret, certificat ou federated credential
Managed identity Identité Entra gérée par Azure, sans secret : system-assigned (liée à une ressource) ou user-assigned (ressource autonome)
Workload identity federation (WIF) Entra fait confiance au jeton OIDC d'un fournisseur externe (Azure DevOps, GitHub) : issuer + subject + audience
Service connection Objet Azure DevOps qui permet à un pipeline d'appeler un service externe (Azure, registre, GitHub…)
PAT Personal access token : jeton d'un utilisateur, avec scopes et expiration (Azure DevOps comme GitHub)
GITHUB_TOKEN Jeton d'installation créé pour chaque job GitHub Actions, limité au dépôt du workflow
GitHub App Application installée sur une organisation ou des dépôts, avec ses propres permissions, indépendante d'un utilisateur
Protected resource Ressource Azure DevOps qu'un pipeline doit être autorisé à utiliser : service connection, agent pool, variable group, secure file, environment, repository
GHAS GitHub Advanced Security, vendu en GitHub Secret Protection et GitHub Code Security
GHAzDO GitHub Advanced Security for Azure DevOps : les mêmes moteurs sur Azure Repos
MSDO Microsoft Security DevOps : extension Azure DevOps et action GitHub qui lancent des analyseurs (Trivy, Checkov, Bandit…)

2. Service principal ou managed identity#

Quelle identité pour qu'un pipeline accède à Azure ?

Critère Service principal (app registration) System-assigned MI User-assigned MI
Où vit l'identité Microsoft Entra ID Sur la ressource (VM, App Service…) Ressource Azure autonome
Cycle de vie Géré par vous Supprimée avec la ressource Géré par vous, partageable
Secret Secret ou certificat, ou federated credential Aucun Aucun (federated credential possible)
Utilisable hors d'Azure Oui (avec WIF) Non Oui, via federated credential
Cas DevOps typique Service connection, workflow GitHub Agent self-hosted sur une VM Azure Plusieurs pipelines ou plusieurs ressources, identité stable
flowchart TD
    A[Le code qui s'authentifie tourne-t-il sur une ressource Azure ?] -->|Oui| B[Une seule ressource ?]
    B -->|Oui| C[System-assigned managed identity]
    B -->|Non, plusieurs| D[User-assigned managed identity]
    A -->|Non : Azure DevOps hébergé, GitHub Actions| E[Workload identity federation]
    E --> F[App registration + federated credential]
    E --> G[User-assigned managed identity + federated credential]
    A -->|Outil ancien sans OIDC| H[Service principal + secret ou certificat, en dernier recours]

À retenir :

  • Une managed identity n'est utilisable que depuis une ressource Azure ; un agent Microsoft-hosted ou un runner GitHub-hosted n'en a pas. Pour eux : workload identity federation, sur une app registration ou une user-assigned managed identity.
  • Le choix entre app registration et user-assigned MI pour la federation tient souvent à l'organisation : créer une app registration demande des droits Entra (Users can register applications ou un rôle comme Application Developer), créer une user-assigned MI demande seulement un rôle Azure sur un resource group.
  • Une app registration ou une user-assigned MI accepte au plus 20 federated credentials.
  • Le rôle Azure (RBAC) se donne au plus petit scope utile : resource group plutôt qu'abonnement.

3. L'authentification côté GitHub#

S'authentifier côté GitHub : du plus sûr au moins sûr

3.1 Le GITHUB_TOKEN#

Avant chaque job, GitHub crée un jeton d'installation exposé en secrets.GITHUB_TOKEN (ou github.token). Il ne vaut que pour le dépôt du workflow et expire à la fin du job.

  • Les droits par défaut se règlent dans Settings > Actions > General > Workflow permissions (organisation ou dépôt) : Read and write permissions ou Read repository contents and packages permissions (le plus sûr).
  • Le bloc permissions (au niveau du workflow ou du job) restreint le jeton. Dès qu'une permission est citée, toutes les autres passent à none. permissions: {} retire tout.
  • Un événement déclenché avec le GITHUB_TOKEN (push, création de PR…) ne lance pas d'autre workflow, sauf workflow_dispatch et repository_dispatch : cela évite les boucles.
# .github/workflows/ci.yml : jeton réduit au strict nécessaire
permissions:
  contents: read            # cloner le dépôt

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write  # ce job seul commente la PR
    steps:
      - uses: actions/checkout@v4
      - run: gh pr comment "$PR" --body "Build OK"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR: ${{ github.event.pull_request.number }}

3.2 GitHub App, OAuth app ou PAT#

Besoin Choix Pourquoi
Agir sur le dépôt du workflow GITHUB_TOKEN Gratuit, éphémère, déjà là
Agir sur d'autres dépôts ou l'organisation, en automatisation GitHub App Permissions fines, dépôts choisis à l'installation, jetons d'installation courts, pas liée à une personne qui part
Agir au nom d'un utilisateur dans une application GitHub App (user access token) ou OAuth app L'app agit avec les droits de l'utilisateur
Script ponctuel d'un utilisateur Fine-grained PAT Dépôts et permissions choisis, expiration obligatoire, l'organisation peut exiger une approbation
Rien d'autre ne marche Classic PAT Scopes larges (repo = tous les dépôts) : à éviter

Dans un workflow, une GitHub App s'utilise en stockant son Client ID (différent de l'App ID) et sa clé privée (secret), puis en générant un jeton d'installation au début du job avec actions/create-github-app-token@v3 (inputs client-id et private-key). Les organisations peuvent restreindre ou exiger l'approbation des fine-grained PAT et bloquer les classic PAT.


4. Service connections et PAT Azure DevOps#

4.1 La service connection Azure Resource Manager#

Une service connection en workload identity federation : aucun secret

Project settings > Service connections > New service connection > Azure Resource Manager. Les options d'identité :

Option Ce qu'Azure DevOps fait À retenir
App registration (automatic) + workload identity federation Crée l'app registration, la federated credential et le rôle Recommandé ; il faut le droit de créer une app et d'attribuer un rôle sur le scope
App registration or managed identity (manual) + WIF Vous créez l'identité et la federated credential, avec l'Issuer et le Subject identifier affichés Quand l'équipe identité est séparée de l'équipe DevOps
Managed identity (agent-assigned) Utilise l'identité de la VM de l'agent self-hosted Agents self-hosted sur Azure seulement
App registration + secret Stocke un secret qui expire Compatibilité ; erreur AADSTS7000215 = secret invalide ou expiré
Publish profile Profil de publication App Service Compatibilité
sequenceDiagram
    participant P as Pipeline (job)
    participant ADO as Azure DevOps
    participant E as Microsoft Entra ID
    participant A as Azure Resource Manager
    P->>ADO: tâche AzureCLI@2 avec azureSubscription: sc-prod
    ADO->>P: jeton OIDC (issuer, subject sc://org/projet/sc-prod)
    P->>E: échange du jeton OIDC (audience api://AzureADTokenExchange)
    E->>E: compare issuer + subject + audience à la federated credential
    E->>P: jeton d'accès Entra (courte durée)
    P->>A: appels ARM avec le rôle RBAC de l'identité
# azure-pipelines.yml : la service connection ne contient aucun secret
steps:
  - task: AzureCLI@2
    inputs:
      azureSubscription: sc-prod       # nom de la service connection ARM en WIF
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: az group show --name rg-app-prod --query id -o tsv

Convertir une connexion à secret : bouton Convert dans la service connection. Il ne fonctionne que si la connexion a été créée automatiquement par Azure DevOps et n'est utilisée que par un projet ; sinon, conversion manuelle (ajouter la federated credential à l'identité). La conversion peut être annulée pendant 7 jours.

Changement d'issuer : les connexions WIF anciennes utilisent l'issuer Azure DevOps (vstoken.dev.azure.com), retiré le 1er juillet 2027 au profit de l'issuer Microsoft Entra (https://login.microsoftonline.com/{tenant-id}). Le pipeline affiche un avertissement ; on convertit depuis la service connection. Hors périmètre : clouds non publics et apps multitenant. Si l'organisation, le projet ou la connexion est renommé, le subject ne correspond plus (AADSTS70021) : mettre à jour la federated credential.

4.2 Sécuriser la service connection elle-même#

Une service connection est une protected resource (voir 6.3) :

  • Security : rôles User (l'utiliser), Creator, Administrator ; donner User aux seules personnes utiles.
  • Pipeline permissions : n'autoriser que les pipelines nommés. Grant access permission to all pipelines (open access) est déconseillé, et Defender for Cloud a une recommandation dédiée : Azure DevOps service connections shouldn't grant access to all pipelines.
  • Approvals and checks : approbation, horaires, Required template, Business hours, branche protégée.
  • Une connexion par environnement (sc-dev, sc-prod), chacune avec son identité et son rôle RBAC.

4.3 Les PAT Azure DevOps#

Un PAT agit au nom de l'utilisateur, avec ses droits limités par des scopes (Code Read, Build Read & execute…) et une expiration (un an au maximum). Microsoft recommande de préférer les jetons Microsoft Entra (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798, l'ID de ressource Azure DevOps) et, pour les pipelines, des service connections en WIF.

Politique Où Effet
Restrict personal access token (PAT) creation Organization settings > Policies (Project Collection Administrators) Seuls les utilisateurs d'une allowlist créent des PAT ; option « packaging seulement » ; bloque les PAT globaux
Restrict global personal access token creation Microsoft Entra, Azure DevOps Administrator Interdit les PAT valables sur toutes les organisations
Restrict full-scoped personal access token creation Idem Interdit les PAT « Full access »
Enforce maximum personal access token lifespan Idem Durée maximale imposée
Revoke leaked PATs Activé par défaut Un PAT trouvé dans un dépôt GitHub public est révoqué automatiquement

Dates à connaître : les PAT globaux (toutes organisations) cessent de fonctionner le 1er décembre 2026 ; l'Azure DevOps OAuth est déprécié (plus de nouvelles apps depuis avril 2025) au profit de Microsoft Entra.


5. Permissions et rôles dans GitHub#

GitHub : rôles d'organisation, rôles de dépôt et outside collaborators

Niveau Rôles À retenir
Organisation Owner, Member, Billing manager, Security manager… Les owners ont un accès admin à tous les dépôts ; en avoir au moins deux, pas plus que nécessaire
Base permissions No permission, Read, Write, Admin Droit minimal de tous les members sur tous les dépôts : le laisser bas (No permission ou Read)
Teams Un rôle par dépôt ; équipes imbriquées Les équipes enfants héritent des accès de l'équipe parente
Dépôt Read, Triage, Write, Maintain, Admin Le droit effectif est le plus élevé reçu (base, équipe, direct)
Custom repository roles Un rôle de base + des permissions ajoutées GitHub Enterprise Cloud seulement

Les cinq rôles de dépôt :

  • Read : lire, cloner, ouvrir des issues et commenter.
  • Triage : gérer issues et PR (labels, assignations) sans écrire de code.
  • Write : pousser, fusionner, gérer les workflows.
  • Maintain : gérer le dépôt sans les actions sensibles ou destructrices.
  • Admin : tout, y compris la sécurité, les accès et la suppression. Seul ce rôle gère les accès au dépôt.

Les protections qui complètent les rôles : branch protection rules et rulesets (voir chapitre 02), CODEOWNERS pour les revues obligatoires, et environments (reviewers requis, branches autorisées, secrets d'environment) pour les déploiements.


6. Permissions et groupes de sécurité dans Azure DevOps#

Azure DevOps : access level, groupes de sécurité et protected resources

6.1 Trois notions à ne pas confondre#

  • Access level : ce que l'utilisateur voit (licence) : Stakeholder, Basic, Basic + Test Plans, Visual Studio subscriber.
  • Groupe de sécurité et permissions : ce qu'il peut faire.
  • Protected resources : ce que chaque pipeline peut utiliser.

Un utilisateur Stakeholder membre de Project Administrators ne peut toujours pas utiliser Repos : l'access level l'emporte.

6.2 Les groupes et l'héritage#

Niveau Groupes intégrés principaux
Organisation (collection) Project Collection Administrators, Project Collection Build Administrators, Project Collection Valid Users, Project-Scoped Users…
Projet Project Administrators, Contributors, Readers, Build Administrators, Release Administrators, Project Valid Users, un groupe par team
  • Les permissions prennent trois états : Allow, Deny, Not set (refus implicite), et s'héritent organisation → projet → objet (dépôt, branche, pipeline, dossier). Un Deny explicite l'emporte en général sur un Allow hérité.
  • Les permissions de Project Collection Administrators ne se modifient pas.
  • Bonne pratique : ajouter des groupes Microsoft Entra aux groupes intégrés plutôt que des utilisateurs un par un.
  • Sécurité par objet : un dépôt ou une branche (Repos > Security), un pipeline ou un dossier de pipelines.

6.3 Les protected resources#

Pour une service connection, un agent pool, un variable group, un secure file, un environment ou un dépôt :

  1. Security (utilisateurs) : rôles User, Creator, Administrator (selon la ressource).
  2. Pipeline permissions : quels pipelines y ont droit ; le premier run d'un pipeline non autorisé attend une autorisation.
  3. Approvals and checks : approbation, branche protégée, Required template, Invoke Azure Function, Business hours… (ordre d'évaluation et Bypass check : chapitre 04, § 10.2).

Réglages de projet utiles (Project settings > Pipelines > Settings) : Limit job authorization scope to current project, Protect access to repositories in YAML pipelines, Limit variables that can be set at queue time.


7. Recommander le bon niveau d'accès#

Profil Azure DevOps GitHub
Métier qui suit et approuve Stakeholder (gratuit, sans limite de nombre) Member avec Read, ou Triage pour trier les issues
Développeur interne Basic (les 5 premiers gratuits) + Contributors Member d'une team avec Write
Testeur qui gère des plans de test Basic + Test Plans —
Abonné Visual Studio Access level Visual Studio subscriber (selon l'abonnement) —
Prestataire externe sur un projet Utilisateur invité (B2B) en Basic, limité au projet Outside collaborator sur les seuls dépôts utiles
Équipe sécurité Lecteur + accès aux alertes Advanced Security Rôle Security manager

Stakeholder (projets privés) : accès partiel à Boards (work items), voir et approuver les releases et les déploiements, pas d'accès à Repos ni à Test Plans. Il ne crée ni ne modifie les pipelines. Les projets publics donnent davantage aux stakeholders, mais Microsoft retire les projets publics (conversion en privé à partir de 2027).

Outside collaborator : personne hors de l'organisation GitHub invitée sur un ou plusieurs dépôts précis, avec un rôle de dépôt. Elle ne voit ni les autres dépôts ni les teams, mais occupe une licence payante dès qu'elle accède à un dépôt privé (sauf sur un plan gratuit). Un admin de dépôt peut inviter des outside collaborators si l'organisation le permet.


8. Projets et équipes dans Azure DevOps#

  • Projet (Organization settings > Projects > New project) : visibilité (privée), processus (Basic, Agile, Scrum, CMMI), services activés (Project settings > Overview : désactiver Boards, Repos, Test Plans… inutiles). Un projet par produit ou domaine ; éviter un projet par équipe.
  • Team (Project settings > Teams > New team) : crée un groupe de sécurité au nom de l'équipe et, si demandé, une area path. Chaque équipe a ses team administrators, ses area paths, ses iterations (300 au maximum par équipe), son backlog, son tableau, ses notifications.
  • Le team administrator configure l'équipe (backlogs, sprints, membres) sans être Project Administrator.
  • Les permissions sur les work items se règlent par area path (Project settings > Boards > Project configuration).
# Azure DevOps CLI (extension azure-devops), à lancer dans une organisation de test
az devops team create --name "Equipe Paiement" --project "Projet-Demo" --org https://dev.azure.com/{organisation}
az devops team list --project "Projet-Demo" --org https://dev.azure.com/{organisation} -o table

9. Key Vault dans les pipelines#

Key Vault range trois types d'objets (voir SC-500, chapitre 02) : secrets (chaînes), keys (clés cryptographiques, l'opération se fait dans le coffre) et certificates (avec leur cycle de vie ; la partie privée est lisible comme secret si elle est exportable).

Méthode Quand Droits de l'identité de la service connection
Variable group lié à Key Vault Plusieurs pipelines partagent les mêmes secrets Get + List (access policy) ou Key Vault Secrets User (RBAC)
Tâche AzureKeyVault@2 Un pipeline lit des secrets au moment voulu Idem
az keyvault … après azure/login (GitHub) Workflow GitHub Actions Rôle RBAC minimal sur le coffre
Opérations sur keys (signer, chiffrer) Signature d'artefacts Key Vault Crypto User
Lecture d'un certificate Déployer un certificat Key Vault Certificate User (ou Secrets User pour la partie privée)

Le variable group lié à Key Vault :

  • seuls les noms des secrets sont mappés ; la valeur est lue à chaque run (une rotation est prise en compte seule) ;
  • un secret ajouté ou supprimé dans le coffre n'apparaît pas tout seul : mettre à jour le variable group ;
  • il ne mappe que des secrets, pas les keys ni les certificates ;
  • Key Vault privé : autoriser les IP statiques d'Azure DevOps de la géographie de l'organisation, ou lire le coffre depuis un agent self-hosted dans le réseau. Un coffre en RBAC derrière un private endpoint n'est pas supporté par le variable group (Azure DevOps n'est pas un trusted service) : utiliser le modèle Vault access policy ou un agent self-hosted.
variables:
  - group: vg-kv-app-prod            # variable group lié à Key Vault

steps:
  - task: AzureKeyVault@2            # alternative : lire au moment voulu
    inputs:
      azureSubscription: sc-prod
      KeyVaultName: kv-app-prod
      SecretsFilter: 'DbPassword'    # jamais '*' en production : le minimum utile
      RunAsPreJob: false
  - script: ./deploy.sh
    env:
      DB_PASSWORD: $(DbPassword)     # un secret n'est jamais exposé seul en variable d'environnement

10. Secrets et authentification sans secret#

Les secrets dans les pipelines : où les ranger, comment les lire

10.1 GitHub Actions vers Azure en OIDC#

  1. Créer une app registration ou une user-assigned MI, lui donner un rôle Azure minimal.
  2. Ajouter une federated credential : issuer https://token.actions.githubusercontent.com, audience api://AzureADTokenExchange, et un entity type : Environment, Branch, Pull request ou Tag (subject repo:{org}/{repo}:environment:production, …:ref:refs/heads/main, …:pull_request, …:ref:refs/tags/v1), sans joker.
  3. Ranger AZURE_CLIENT_ID, AZURE_TENANT_ID et AZURE_SUBSCRIPTION_ID en secrets (ce ne sont pas des mots de passe, mais on évite de les exposer).
name: deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write      # obligatoire pour demander le jeton OIDC
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production          # le subject de la federated credential vise cet environment
    steps:
      - uses: actions/checkout@v4
      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - run: az group show --name rg-app-prod

Le subject doit correspondre exactement : une federated credential de type Branch main refuse un job lancé sur un environment, et une erreur de saisie dans le subject ne se voit qu'à l'échange du jeton.

10.2 Les secrets GitHub#

Portée Usage
Organization secrets Partagés, limités à des dépôts choisis
Repository secrets Un dépôt
Environment secrets Lus seulement par un job qui cible l'environment, après ses protections (reviewers requis, branches autorisées)

Les secrets ne sont pas transmis aux workflows déclenchés depuis un fork (sauf le GITHUB_TOKEN, en lecture seule).

10.3 Les secret variables d'Azure Pipelines#

  • Définies dans l'interface du pipeline (cadenas), dans un variable group ou dans Key Vault ; chiffrées (RSA 2048).
  • Pas mappées automatiquement en variables d'environnement des scripts : les passer par env:.
  • Créées dans un script avec la commande de journalisation ##vso[task.setvariable variable=nom;issecret=true]valeur.
  • Non transmises aux builds de fork par défaut (Make secrets available to builds of forks désactivé).

11. Les fichiers sensibles : secure files#

Certificats .pfx, profils de provisioning, clés SSH, fichiers .p12 : ils ne vont jamais dans le dépôt. Pipelines > Library > Secure files :

  • 10 MB maximum par fichier, chiffrés au repos ;
  • protected resource : Security (rôles), Pipeline permissions, Approvals and checks ;
  • téléchargés par DownloadSecureFile@1 (agent 2.182.1 ou plus récent) dans un dossier temporaire, et supprimés à la fin du job.
steps:
  - task: DownloadSecureFile@1
    name: certSigning                  # nom de référence pour lire le chemin
    inputs:
      secureFile: 'signing-cert.pfx'
  - script: ./sign.sh "$(certSigning.secureFilePath)"

Alternatives : un certificate Key Vault lu au moment du déploiement ; côté GitHub, un fichier encodé en base64 dans un secret (limité en taille) ou, mieux, la récupération depuis Key Vault après azure/login.


12. Empêcher les fuites d'informations sensibles#

Le masquage des journaux est un filet, pas une protection : Azure Pipelines et GitHub masquent la valeur exacte d'un secret déclaré, mais pas une sous-chaîne, pas une valeur transformée (base64, URL-encodée) ni un secret stocké en JSON ou en données structurées.

Règle Azure Pipelines GitHub Actions
Pas de secret quand c'est possible Service connection en WIF OIDC (id-token: write)
Moindre privilège du jeton du job Limit job authorization scope, permissions du Build Service permissions au plus juste
Ne jamais afficher Pas d'echo, pas de set -x, pas en argument de ligne de commande (visible dans la liste des processus) Idem ; ::add-mask:: pour une valeur calculée
Passer les secrets proprement env: explicite ; System.AccessToken seulement si nécessaire env: plutôt que ${{ secrets.X }} dans le texte d'un run
Contributions externes Pas de secrets pour les builds de fork Prudence avec pull_request_target (code du fork + secrets)
Variables modifiables au lancement Limit variables that can be set at queue time, settableVariables sur les steps inputs typés de workflow_dispatch
Dépendances de la chaîne Extensions validées, Required template Actions épinglées par SHA, liste d'actions autorisées
Détection GHAzDO push protection, secret scanning Push protection, secret scanning
Agents Agents éphémères (Microsoft-hosted, Managed DevOps Pools) Runners GitHub-hosted ; jamais de self-hosted sur un dépôt public
# Azure Pipelines : un step qui ne peut modifier aucune variable
steps:
  - script: ./outil-tiers.sh
    target:
      settableVariables: none

Un secret a fuité : le révoquer et le remplacer d'abord (Key Vault, rotation), puis le retirer du code ; le supprimer de l'historique Git ne suffit pas, il a pu être copié.


13. La stratégie d'analyse#

Analyser au plus tôt : chaque étape a son contrôle

Type d'analyse Ce qu'elle trouve Moment idéal
Secret scanning Clés, jetons, mots de passe dans le code et l'historique Push (push protection) puis en continu
Dependency scanning (SCA) Dépendances open source vulnérables (CVE) PR (dependency review) et build
Code scanning (SAST) Failles dans le code (injection, XSS…) : CodeQL PR et branche par défaut, planifié
License scanning Licences interdites par la politique (GPL dans un produit propriétaire…) PR : dependency review sur GitHub ; rien de documenté dans GHAzDO (outil tiers)
IaC scanning Mauvaises configurations Terraform, Bicep, ARM, Kubernetes PR et build (Checkov, Template Analyzer, Terrascan)
Container scanning CVE des images, de l'OS de base aux paquets Build, registre, runtime

Principes d'une stratégie :

  • Shift left : bloquer au plus tôt (push, PR), là où corriger coûte le moins ; garder une analyse planifiée pour les CVE découvertes après coup.
  • Bloquer seulement ce qui est grave (seuil de sévérité) pour ne pas noyer les équipes ; le reste en alertes suivies.
  • Rendre les contrôles obligatoires : branch policy Build validation (Azure Repos), required status checks ou rulesets (GitHub).
  • Centraliser les résultats (SARIF, Defender for Cloud) pour une vue de posture unique.

14. Defender for Cloud DevOps security#

Defender for Cloud DevOps security : du dépôt à la production

Connecter : Defender for Cloud > Environment settings > Add environment : Azure DevOps, GitHub ou GitLab. Le connecteur est une ressource Azure (abonnement, resource group) ; il faut Security Admin côté Azure et des droits d'administration côté plateforme (Project Collection Administrator, owner d'organisation GitHub).

Fonction Foundational CSPM (gratuit) Defender CSPM (payant)
Inventaire des dépôts, console DevOps security Oui Oui
Recommandations de posture DevOps (configuration des organisations, service connections…) Oui Oui
Remontée des findings (GHAS, GHAzDO, MSDO) Oui Oui
PR annotations — Oui
Code-to-cloud mapping, attack path, cloud security explorer — Oui
Agentless code scanning — Oui
Intégration GHAS (runtime → GitHub) — Oui (+ licence GHAS)

PR annotations : DevOps security > sélectionner les dépôts > Configure > activer les annotations. Sur Azure DevOps, seulement pour les findings IaC, avec une branch policy Build validation qui lance MSDO. Sur GitHub, elles demandent GHAS. Pas d'annotations pour GitLab.

Microsoft Security DevOps dans le pipeline (extension Microsoft Security DevOps à installer depuis le Marketplace) :

steps:
  - task: MicrosoftSecurityDevOps@1
    inputs:
      categories: 'IaC,containers'   # code, artifacts, IaC, containers
      break: true                    # échoue si un résultat bloquant est trouvé
      publish: true                  # publie le SARIF dans l'onglet Scans

Côté GitHub : l'action microsoft/security-devops-action@v1, puis github/codeql-action/upload-sarif pour envoyer le SARIF vers code scanning. Outils exécutés selon la catégorie : AntiMalware, Bandit, BinSkim, Checkov, ESLint, IaCFileScanner, Template Analyzer, Terrascan, Trivy.


15. GitHub Advanced Security (GHAS) et GHAzDO#

15.1 Sur GitHub#

Produit Fonctions Activation
GitHub Secret Protection Secret scanning (dépôt et historique), push protection, validity checks, custom patterns Settings > Advanced Security (dépôt) ou security configurations (organisation)
GitHub Code Security Code scanning (CodeQL, outils tiers en SARIF), dependency review, Copilot Autofix, security campaigns Idem
Gratuit pour tous Dependabot alerts et security updates, dependency graph ; sur les dépôts publics, secret scanning et code scanning —

Code scanning : default setup (GitHub choisit langages et requêtes, rien à écrire) ou advanced setup (un workflow codeql.yml personnalisable : langages, build, requêtes security-extended, planification).

15.2 Sur Azure DevOps (GHAzDO)#

Vendu en deux produits : GitHub Secret Protection for Azure DevOps et GitHub Code Security for Azure DevOps (l'ancien bundle reste pour les clients existants). Facturé par committer actif (un push dans les 90 derniers jours), dédoublonné par abonnement Azure. Seulement Azure DevOps Services et dépôts Git.

  • Activer : au niveau du dépôt (Project settings > Repositories > {dépôt} > Settings), du projet ou de l'organisation, en confirmant Begin billing.
  • Push protection : bloque les pushs qui contiennent un secret ; n'évalue que les pushs après l'activation. Le secret scanning du dépôt, lui, analyse aussi l'historique.
  • Dependency scanning et code scanning (CodeQL) : des tâches de pipeline. Les alertes apparaissent dans Repos > Advanced Security.
# GHAzDO : dependency scanning et CodeQL dans un pipeline Azure Repos
steps:
  - task: AdvancedSecurity-Codeql-Init@1
    inputs:
      languages: 'csharp'
      enableAutomaticCodeQLInstall: true   # utile sur un agent self-hosted
  - task: DotNetCoreCLI@2
    inputs:
      command: build
  - task: AdvancedSecurity-Dependency-Scanning@1
  - task: AdvancedSecurity-Codeql-Analyze@1

Pour un langage interprété (JavaScript, Python), buildtype: none évite l'étape de build. AdvancedSecurity-Publish@1 publie dans Advanced Security un SARIF produit par un autre outil.


16. Intégrer GHAS à Defender for Cloud#

Intégration GA depuis le 3 mai 2026. Elle relie le code (GHAS) au runtime (Defender for Cloud) dans les deux sens.

Prérequis Rôles
Connecteur GitHub dans Defender for Cloud Security Admin sur l'abonnement
Licence GHAS sur les dépôts connectés Owner de l'organisation GitHub
Defender CSPM activé sur l'abonnement Security Copilot facultatif (remédiation automatisée)
Clouds commerciaux seulement —

Ce qu'elle apporte :

  • Code-to-runtime mapping automatique : quelle image en production vient de quel dépôt.
  • Runtime risk factors dans les alertes GHAS : Internet Exposure, Sensitive Data, Critical Resources, Lateral Movement (issus de l'attack path analysis de Defender CSPM).
  • Copilot Autofix et security campaigns lancées depuis Defender for Cloud ; création d'issues GitHub assignées aux CODEOWNERS.
  • Synchronisation bidirectionnelle du statut des issues.

Mise en route : connecteur GitHub, agentless code scanning activé, GHAS actif sur les dépôts (Advanced security status : On dans DevOps security). Comptez jusqu'à 24 heures avant les premiers résultats.


17. Analyser les conteneurs et CodeQL dans un conteneur#

Analyser les images :

  • dans le pipeline : Trivy via MSDO (catégorie containers), ou le Defender CLI (tâche MicrosoftDefenderCLI@2, demande Defender CSPM) qui analyse l'image avant le push et remonte les résultats dans Defender for Cloud ;
  • dans le registre et au runtime : Defender for Containers (analyse des images d'Azure Container Registry, détection au runtime, gated deployment sur Kubernetes) ;
  • Dependabot suit aussi les images de base d'un Dockerfile (écosystème docker).

CodeQL dans un conteneur : quand le build a besoin d'une image précise (compilateur, SDK), le job entier tourne dans le conteneur. Toutes les étapes doivent s'y exécuter, et les images Alpine (musl) ne sont pas supportées.

name: codeql-container
on: [push, pull_request]

jobs:
  analyze:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/dotnet/sdk:8.0   # image qui contient la chaîne de build
    permissions:
      security-events: write   # publier les résultats de code scanning
      actions: read
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v4
        with:
          languages: csharp
      - run: dotnet build
      - uses: github/codeql-action/analyze@v4

18. Dependabot alerts#

flowchart LR
    A[Dependency graph<br>manifestes, lockfiles] --> C{Dépendance vulnérable ?}
    B[GitHub Advisory Database] --> C
    C -->|oui| D[Dependabot alert<br>branche par défaut]
    D --> E[Notification<br>write, maintain, admin]
    D --> F[Dependabot security update<br>PR de correction]
    D --> G[Auto-triage rules<br>ignorer, reporter]
  • Dependabot alerts : comparent le dependency graph de la branche par défaut à la GitHub Advisory Database ; gratuites, activables par dépôt (Settings > Advanced Security) ou pour toute l'organisation. Notifications aux personnes qui ont Write, Maintain ou Admin et suivent le dépôt.
  • Dependabot security updates : ouvrent une PR qui monte la dépendance à la version corrigée.
  • Dependabot version updates : maintiennent les dépendances à jour, même sans faille, via .github/dependabot.yml.
  • Auto-triage rules : ignorer ou reporter automatiquement des alertes (par exemple, faible impact sur les dépendances de développement).
  • Dependency review (dans la PR) : montre les dépendances ajoutées, leur sévérité et leur licence ; l'action actions/dependency-review-action fait échouer la PR selon fail-on-severity (low à critical) et allow-licenses ou deny-licenses (identifiants SPDX). Dépôts publics, ou privés avec GitHub Code Security.
# .github/dependabot.yml : mises à jour de versions, en plus des alertes
version: 2
updates:
  - package-ecosystem: "nuget"
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
# .github/workflows/dependency-review.yml : bloquer une PR qui ajoute une faille ou une licence refusée
on: pull_request
permissions:
  contents: read
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v4
        with:
          fail-on-severity: high
          allow-licenses: MIT, Apache-2.0, BSD-3-Clause

Sur Azure Repos, Dependabot n'existe pas nativement : on utilise le dependency scanning de GHAzDO.


19. Labs conseillés#

Dans un abonnement et une organisation de test, jamais en production. Coûts : GHAzDO est facturé par committer actif (désactiver à la fin), Defender CSPM est payant (le connecteur seul, en Foundational CSPM, est gratuit). Aucun secret réel dans les dépôts : des valeurs factices.

  1. Service connection WIF (30 min) : créer une connexion ARM App registration (automatic) sur un resource group de test, lancer un AzureCLI@2, regarder l'Issuer et le Subject identifier dans la connexion et la federated credential dans Entra. Restreindre les Pipeline permissions à un seul pipeline. Résultat attendu : un deuxième pipeline attend une autorisation.
  2. GitHub OIDC (30 min) : federated credential de type Environment production, workflow azure/login@v2 avec id-token: write. Lancer depuis une autre branche sans environment. Résultat attendu : AADSTS70021, puis succès avec l'environment.
  3. Key Vault et secure files (30 min) : variable group lié à un coffre de test (Key Vault Secrets User), un secure file factice téléchargé par DownloadSecureFile@1. Afficher la valeur avec echo, puis en base64. Résultat attendu : la première est masquée, la seconde ne l'est pas (d'où la règle « ne jamais afficher »).
  4. GHAS sur un dépôt public (30 min, gratuit) : activer secret scanning et push protection, tenter de pousser un faux jeton de test, activer CodeQL default setup et Dependabot alerts sur un projet avec une vieille dépendance. Résultat attendu : push bloqué, alertes Dependabot et PR de correction.
  5. Defender for Cloud (20 min) : connecteur GitHub ou Azure DevOps en Foundational CSPM, ouvrir DevOps security, lire les recommandations. Supprimer le connecteur à la fin.

20. Pièges d'examen#

Situation dans la question Réponse attendue Pourquoi
Pipeline Microsoft-hosted qui doit accéder à Azure sans secret Workload identity federation Pas de managed identity sur un agent hébergé
Agent self-hosted sur une VM Azure, une seule VM System-assigned MI (agent-assigned) Cycle de vie lié à la VM
Même identité pour plusieurs ressources ou pipelines User-assigned MI Ressource autonome et partageable
Service connection en erreur AADSTS7000215 Secret expiré : convertir en WIF Plus de secret à faire tourner
Bouton Convert grisé Connexion manuelle ou partagée entre projets Convert = créée automatiquement, un seul projet ; annulable 7 jours
Avertissement « Azure DevOps issuer deprecated » Convertir vers l'issuer Microsoft Entra Retrait le 01/07/2027
Une service connection est utilisable par tous les pipelines Retirer l'open access, autoriser les pipelines un par un Recommandation Defender for Cloud
Empêcher les PAT « Full access » Politique Entra Restrict full-scoped PAT (Azure DevOps Administrator) —
Limiter la création de PAT à quelques personnes Restrict personal access token (PAT) creation avec allowlist Politique d'organisation
Workflow qui pousse un commit : le workflow suivant ne part pas Normal avec le GITHUB_TOKEN ; GitHub App si nécessaire Anti-boucle
Automatiser sur 30 dépôts sans dépendre d'un employé GitHub App Pas un PAT d'utilisateur
permissions: contents: read et le job ne peut plus commenter la PR Les permissions non citées passent à none Ajouter pull-requests: write
azure/login en OIDC échoue sans message clair id-token: write manquant ou subject différent Le subject doit correspondre exactement
Prestataire sur deux dépôts GitHub Outside collaborator Pas member de l'organisation
Métier qui doit approuver des releases, gratuitement Stakeholder Pas besoin de Basic
Stakeholder qui ne voit pas Repos malgré Project Administrators L'access level l'emporte Passer en Basic
Accès aux dépôts des autres projets par le jeton du build Limit job authorization scope to current project Moindre privilège
Variable group Key Vault : nouveau secret absent Mettre à jour le variable group Seuls les noms choisis sont mappés
Coffre RBAC derrière un private endpoint pour un variable group Non supporté : access policy ou agent self-hosted Azure DevOps n'est pas un trusted service
Certificat .pfx pour signer dans le pipeline Secure file + DownloadSecureFile@1, ou certificate Key Vault Jamais dans le dépôt
Secret masqué, mais visible en base64 dans les logs Le masquage ne couvre pas les valeurs transformées Ne jamais afficher
Secret dans une variable non mappée en env: Le script ne la voit pas Mappage explicite obligatoire
Push protection activée : un vieux secret est encore là Push protection ne voit que les nouveaux pushs ; secret scanning trouve l'historique —
Secret découvert dans le code Révoquer et remplacer, puis nettoyer Il a pu être copié
PR annotations Defender sur Azure DevOps pour du C# Non : IaC seulement + Build validation + Defender CSPM ; sur GitHub, il faut GHAS
Code-to-cloud, attack path depuis le code Defender CSPM Pas en Foundational CSPM
Intégration GHAS ↔ Defender for Cloud Connecteur GitHub + licence GHAS + Defender CSPM GA mai 2026
Dependabot sur Azure Repos N'existe pas : GHAzDO dependency scanning —
CodeQL dans un conteneur Alpine Non supporté musl
Bloquer une PR qui ajoute une dépendance GPL Dependency review avec allow-licenses ou deny-licenses License scanning (les deux options s'excluent)

21. Scénarios corrigés#

1. Contoso déploie depuis Azure Pipelines (agents Microsoft-hosted) avec une service connection à secret ; les déploiements échouent tous les ans à l'expiration. Que faire ?

Convertir la connexion en workload identity federation (bouton Convert si elle a été créée automatiquement et sert à un seul projet, sinon federated credential manuelle). Plus de secret, réversible 7 jours. Vérifier qu'elle utilise l'issuer Microsoft Entra.

2. L'équipe sécurité découvre qu'une service connection vers l'abonnement de production est utilisable par tous les pipelines du projet, y compris ceux des stagiaires. Corrections ?

Retirer Grant access permission to all pipelines, autoriser uniquement le pipeline de déploiement, donner le rôle User aux seules personnes utiles, ajouter des approvals and checks (approbation, branche main protégée, Required template), et un rôle RBAC limité au resource group.

3. Un workflow GitHub Actions doit déployer sur Azure sans aucun secret Azure stocké, et seulement depuis l'environment production qui exige une approbation. Configuration ?

App registration (ou user-assigned MI) avec une federated credential Environment = production ; dans le workflow, permissions: id-token: write, environment: production, azure/login@v2 avec client, tenant et subscription IDs. Le job attend l'approbation de l'environment avant d'obtenir le jeton.

4. Un bot doit ouvrir des PR dans 40 dépôts de l'organisation chaque nuit. Le développeur propose son PAT classique. Mieux ?

Une GitHub App installée sur les 40 dépôts, avec seulement Contents et Pull requests en écriture ; le workflow génère un jeton d'installation court. Le PAT donne repo sur tout ce que voit le développeur et casse à son départ.

5. Un prestataire externe doit corriger du code dans deux dépôts GitHub privés pendant trois mois, sans voir le reste.

L'inviter en outside collaborator sur ces deux dépôts avec le rôle Write (ou Triage s'il ne pousse pas), sans l'ajouter à l'organisation ; retirer l'accès à la fin.

6. Vingt responsables métier doivent suivre les work items et approuver les déploiements en production, sans licence payante.

Access level Stakeholder : gratuit, sans limite, approbation des releases et déploiements, Boards partiel. Pas d'accès à Repos, ce qui n'est pas leur besoin.

7. Les secrets d'une application sont dans Key Vault, et cinq pipelines les utilisent. Le coffre est derrière un private endpoint en modèle RBAC. Le variable group lié refuse de fonctionner.

Ce cas n'est pas supporté (Azure DevOps n'est pas un trusted service). Options : passer le coffre en Vault access policy et autoriser les IP statiques Azure DevOps de la géographie, ou lire les secrets depuis un agent self-hosted dans le réseau avec AzureKeyVault@2.

8. Un pipeline doit signer une application avec un certificat .pfx de 4 MB.

Secure file (≤ 10 MB) protégé par pipeline permissions et approbation, téléchargé avec DownloadSecureFile@1 et utilisé via $(nom.secureFilePath) ; le mot de passe du .pfx en secret Key Vault. Le fichier est supprimé à la fin du job.

9. Une clé d'API a été poussée hier dans un dépôt Azure Repos. Le RSSI veut éviter que cela se reproduise et savoir s'il y en a d'autres.

D'abord révoquer et remplacer la clé. Puis activer GitHub Secret Protection for Azure DevOps : push protection pour bloquer les prochains pushs, secret scanning du dépôt pour trouver les secrets déjà présents dans l'historique.

10. Une banque veut, sur Azure Repos, une analyse SAST et des dépendances à chaque PR, avec des résultats visibles dans Defender for Cloud.

GitHub Code Security for Azure DevOps : tâches AdvancedSecurity-Codeql-Init@1, AdvancedSecurity-Dependency-Scanning@1, AdvancedSecurity-Codeql-Analyze@1 dans un pipeline lancé par une branch policy Build validation ; connecteur Azure DevOps dans Defender for Cloud pour remonter les findings.

11. Les développeurs reçoivent des centaines d'alertes de vulnérabilité sur des images qui ne sont même pas déployées. Comment prioriser ?

Intégration GHAS ↔ Defender for Cloud (connecteur GitHub, licence GHAS, Defender CSPM) : les alertes reçoivent des runtime risk factors (exposition Internet, données sensibles) ; filtrer sur elles et lancer une security campaign.

12. Le build d'un projet C++ a besoin d'une chaîne de compilation précise fournie par une image Docker interne, et l'équipe veut CodeQL.

Workflow CodeQL avec container: image: pour que toutes les étapes tournent dans l'image, permissions: security-events: write, codeql-action/init@v4, la commande de build, puis analyze@v4. Image Debian ou Ubuntu, pas Alpine.


22. Pour aller plus loin#

  • Managed DevOps Pools : agents Azure éphémères dans votre VNet, utiles pour atteindre un Key Vault privé (voir chapitre 04).
  • Security configurations GitHub : appliquer GHAS et Dependabot à toutes les organisations d'une entreprise d'un coup.
  • Artifact attestations et provenance de build (SLSA) sur GitHub, en complément de l'intégration Defender for Cloud.
  • Conditional Access sur Azure DevOps (connexion des utilisateurs) et politique de restriction des invités.
  • Azure Policy et Defender for Cloud pour la conformité de ce qui est déployé : chapitre 06 et SC-500, chapitre 10.
  • Mesurer la sécurité (alertes ouvertes, délai de correction) : chapitre 01.

23. Fiche à retenir#

  • Ressource Azure → managed identity (system si une ressource, user-assigned si partagée) ; CI hors d'Azure → workload identity federation (app registration ou user-assigned MI, 20 federated credentials max).
  • Service connection ARM : WIF automatique recommandé ; Convert = créée automatiquement + un seul projet, réversible 7 jours ; issuer Azure DevOps retiré le 01/07/2027.
  • Service connection = protected resource : rôle User, pipeline permissions (pas d'open access), approvals and checks.
  • PAT Azure DevOps : scopes, ≤ 1 an, politiques d'organisation et Entra ; PAT globaux morts le 01/12/2026 ; préférer les jetons Entra.
  • GITHUB_TOKEN : dépôt du workflow, fin du job, permissions (les non citées = none), ne déclenche pas d'autre workflow.
  • GitHub App > fine-grained PAT > classic PAT.
  • GitHub : Read, Triage, Write, Maintain, Admin ; owners = admin partout ; base permissions basses ; custom roles = Enterprise Cloud ; externe = outside collaborator.
  • Azure DevOps : access level (voit) ≠ groupe (fait) ≠ protected resource (pipeline) ; Allow, Deny, Not set ; Stakeholder gratuit, approuve, pas de Repos.
  • Team = groupe de sécurité + area path + iterations + team administrator.
  • Variable group Key Vault : noms mappés, valeurs lues à chaque run, secrets seulement, Get + List ou Secrets User ; RBAC + private endpoint non supporté.
  • GitHub OIDC : id-token: write, issuer token.actions.githubusercontent.com, subject exact (environment, branche, PR, tag).
  • Secure files : 10 MB, DownloadSecureFile@1, $(nom.secureFilePath), supprimés en fin de job.
  • Fuites : pas d'écho, pas en ligne de commande, env: explicite, pas de secrets pour les forks, masquage ≠ protection, fuite = révoquer d'abord.
  • Scans : secret (push), dépendances et licences (PR), code CodeQL (PR), IaC, conteneurs ; bloquer par seuil.
  • Defender for Cloud : connecteur gratuit (inventaire, recommandations) ; Defender CSPM = PR annotations (ADO : IaC + Build validation ; GitHub : GHAS), code-to-cloud, intégration GHAS (GA 03/05/2026).
  • GHAzDO : Secret Protection / Code Security, par committer actif, push protection après activation, tâches AdvancedSecurity-*.
  • CodeQL en conteneur : container:, security-events: write, toutes les étapes dedans, pas Alpine.
  • Dependabot alerts : dependency graph + Advisory Database, branche par défaut, security updates en PR, auto-triage ; dependency review bloque la PR.

Références#

Toutes lues le 21/10/2026, points incertains revérifiés le 22/10/2026 :

Sur cette page
Échap

Tape au moins 2 lettres. Plusieurs mots : tous doivent apparaître.