Photo par Adam Sherez

Définir des policies de branche avec de Terraform dans Azure DevOps

Sécurisez vos branches!

Créé par Damien Aicheh le 28/10/2022 · 10 mins

Dans ce tutoriel, nous allons configurer les policies à appliquer pour chaque repository dans notre projet Azure DevOps à l’aide de Terraform !

Ce tutoriel fait partie d’une série complète de tutoriels sur la configuration d’Azure DevOps à l’aide de Terraform. Vous pouvez télécharger le projet de la partie précédente et suivre.

Cas d’utilisation

Dans un tutoriel précédent vous avez créé vos repository, mais pour y contribuer en utilisant les Pull Requests, vous devez configurer une liste de bonnes pratiques pour éviter que du mauvais code ne soit déployé en production. Donc, pour y parvenir, vous pouvez utiliser le concept de policies et l’appliquer à toutes les branches dont vous avez besoin.

Pour les besoins de ce tutoriel, nous appliquerons les policies uniquement sur la branche par défaut, cependant il est vraiment recommandé de l’appliquer sur chaque branche qui a du sens pour votre projet.

Type de policies

Vous avez plusieurs policies à définir pour verrouiller et standardiser vos Pull Requests :

  • Exiger un nombre minimum de relecteurs
  • Vérifier les work items liés
  • Vérifier la résolution des commentaires
  • Limiter les types de merge
  • Ajout d’une validation de build
  • Vérifications de statut
  • Relecteurs automatiquement inclus

Voyons comment configurer les cinq premiers en créant un nouveau fichier appelé repos_policies.tf.

Notez également dans les prochains exemples de codes, que vous devez ajouter la propriété depends_on pour chacune des policies déclarées, car les fichiers par défaut que vous avez ajoutés dans le repository doivent être poussés avant d’activer les policies. Si vous ne le faites pas, vous obtiendrez une erreur vous indiquant que les policies sont déjà définies et que vous ne pouvez pas pousser vos fichiers par défaut sans passer par une Pull Request, ce qui n’est pas le comportement que vous souhaitez lors de l’initialisation du repository.

## Nombre minimal de relecteurs

Comme vous pouvez le voir ci-dessous, vous définissez un azuredevops_branch_policy_min_reviewers et parcourez une liste de repositories comme nous l’avons défini dans un tutoriel précédent.

resource "azuredevops_branch_policy_min_reviewers" "this" {
  count      = length(var.repositories)
  project_id = azuredevops_project.this.id

  enabled  = true
  blocking = true

  settings {
    reviewer_count                         = 1
    submitter_can_vote                     = false
    last_pusher_cannot_approve             = true
    allow_completion_with_rejects_or_waits = false
    on_push_reset_approved_votes           = false
    on_last_iteration_require_vote         = false

    scope {
      repository_id  = azuredevops_git_repository.this[count.index].id
      repository_ref = azuredevops_git_repository.this[count.index].default_branch
      match_type     = "Exact"
    }
  }

  depends_on = [
    azuredevops_git_repository_file.default_pipeline,
    azuredevops_git_repository_file.default_gitignore,
  ]
}

Vérifier les work items liés

resource "azuredevops_branch_policy_work_item_linking" "this" {
  count      = length(var.repositories)
  project_id = azuredevops_project.this.id

  enabled  = true
  blocking = true

  settings {

    scope {
      repository_id  = azuredevops_git_repository.this[count.index].id
      repository_ref = azuredevops_git_repository.this[count.index].default_branch
      match_type     = "Exact"
    }
  }

  depends_on = [
    azuredevops_git_repository_file.default_pipeline,
    azuredevops_git_repository_file.default_gitignore,
  ]
}

Ci-dessus, vous demandez à l’utilisateur d’ajouter le work item lié à la branche pour une Pull Request. Ceci est utile pour comprendre le code ajouté.

Vérifier la résolution des commentaires

Maintenant, quelque chose de vraiment important concernant les Pull Requests, assurez-vous que les commentaires ont tous été résolus avant de pouvoir les fusionner :

resource "azuredevops_branch_policy_comment_resolution" "this" {
  count      = length(var.repositories)
  project_id = azuredevops_project.this.id

  enabled  = true
  blocking = true

  settings {

    scope {
      repository_id  = azuredevops_git_repository.this[count.index].id
      repository_ref = azuredevops_git_repository.this[count.index].default_branch
      match_type     = "Exact"
    }
  }

  depends_on = [
    azuredevops_git_repository_file.default_pipeline,
    azuredevops_git_repository_file.default_gitignore,
  ]
}

Limiter les types de merge

En fonction des règles que vous avez fixées avec votre équipe, il est important de définir le type de fusion que vous souhaitez pour chaque branche. Par exemple, le squash peut être bon lorsque vous fusionnez une fonctionnalité sur la branche develop. Ainsi, vous vous retrouvez avec un seul commit avec un message formaté au lieu d’avoir tous les commits de la branche de la fonctionnalité, ce qui peut être un peu brouillon.

resource "azuredevops_branch_policy_merge_types" "this" {
  count      = length(var.repositories)
  project_id = azuredevops_project.this.id

  enabled  = true
  blocking = true

  settings {
    allow_squash                  = true
    allow_rebase_and_fast_forward = false
    allow_basic_no_fast_forward   = true
    allow_rebase_with_merge       = false

    scope {
      repository_id  = azuredevops_git_repository.this[count.index].id
      repository_ref = azuredevops_git_repository.this[count.index].default_branch
      match_type     = "Exact"
    }
  }

  depends_on = [
    azuredevops_git_repository_file.default_pipeline,
    azuredevops_git_repository_file.default_gitignore,
  ]
}

Ajout d’une validation de build

Le dernier que vous verrez est probablement le plus important. L’ajout d’une validation de build garantit qu’un pipeline Azure DevOps a exécuté votre code. Ce pipeline doit avoir au minimum, l’exécution de vos tests unitaires, le build du projet et si vous le pouvez, d’autres outils de scan comme Sonar, Checkmarks, etc.. pour valider la qualité de votre code.

resource "azuredevops_branch_policy_build_validation" "this" {
  count      = length(var.repositories)
  project_id = azuredevops_project.this.id

  enabled  = true
  blocking = true

  settings {
    display_name        = "Pull Request Check"
    build_definition_id = azuredevops_build_definition.build_definitions[count.index].id
    valid_duration      = 720 # minutes => 12 hours

    scope {
      repository_id  = azuredevops_git_repository.this[count.index].id
      repository_ref = azuredevops_git_repository.this[count.index].default_branch
      match_type     = "Exact"
    }
  }

  depends_on = [
    azuredevops_git_repository_file.default_pipeline,
    azuredevops_git_repository_file.default_gitignore,
  ]
}

Vous pouvez trouver toutes les policies disponibles dans la documentation de Terraform.

Executer Terraform

Vous pouvez maintenant exécuter la nouvelle commande de planification de Terraform comme ceci :

terraform plan -var-file=env.tfvars --out=plan.out

Et puis appliquez-le plan:

terraform apply plan.out

En conséquence, tous vos repositories auront des stratégies dans la branche par défaut et tout le nouveau code ne sera ajouté à cette branche que par une Pull Request.

Touche finale

Vous avez maintenant vos référentiels de branche par défaut protégés par certaines politiques utilisant Terraform. Vous trouverez le code source complet dans ce dépôt Github.

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !