Blog d'Obi Madu
Retour à tous les articles
InfrastructureDevOpsProjects

Mise en œuvre du flux de travail central de Terraform en collaboration, via Github Pull Requests, Actions, Bot, Environments et un Backend Distant.

Mettez en œuvre un flux de travail Terraform collaboratif à l'aide de GitHub Actions, de pull requests et d'un backend distant pour les équipes de production.

Mise en œuvre du flux de travail central de Terraform en collaboration, via Github Pull Requests, Actions, Bot, Environments et un Backend Distant.

Bonjour ! et bienvenue !! Dans ce projet, nous allons créer une infrastructure simple avec Terraform tout en mettant en œuvre le flux de travail central de Terraform (qui consiste à écrire, planifier et appliquer) en collaboration pour un environnement de production. En termes simples, nous allons déployer une infrastructure avec Terraform d'une manière qui permet à une équipe d'ingénieurs d'introduire des modifications dans le système, de les examiner en détail, de les approuver ou de les refuser et (si elles sont approuvées) de les déployer en production.

Le système que nous allons construire dans ce projet sera adapté aux opérations de certaines petites et moyennes organisations (avec ou sans l'ajout possible de quelques éléments supplémentaires dont nous parlerons à la fin du projet).

Pour les besoins de ce projet, vous êtes un Ingénieur Infrastructure chargé de créer l'infrastructure initiale pour une entreprise de taille moyenne. Nous commencerons par créer notre infrastructure à partir de zéro, en travaillant avec le contrôle de version (Git) tout au long, en amorçant notre système via un Backend Local sur notre station de travail, en le migrant vers un Backend Distant pour permettre la collaboration et en créant des scripts pour automatiser l'ensemble du processus d'Intégration Continue et de Déploiement Continu.

Nous allons :

  • Créer une infrastructure de base avec Terraform sur DigitalOcean & Cloudflare
  • Configurer un Backend Distant Google Cloud Storage pour permettre la collaboration
  • Configurer l'accès à notre backend GCS via Github Environments et les flux de travail Actions
  • Créer des scripts de flux de travail Actions pour Initialiser, Valider, Prévisualiser et Appliquer nos modifications d'infrastructure.
  • Utiliser Github Pull Requests et le Github Bot pour les examens (et approbations ou refus) des modifications d'infrastructure proposées.

Voici le dépôt du projet sur Github : https://github.com/obiMadu/terraform-github-workflow

Plongeons directement 🚀

1. Créer notre Infrastructure via Terraform

Infrastructure

Nous allons créer l'infrastructure très simple illustrée dans le diagramme ci-dessus. Un simple serveur web exécutant Nginx sur Digital Ocean avec une adresse ipv4 et un DNS A record sur une zone Cloudflare qui pointe un sous-domaine vers lui. Nous y parviendrons en créant seulement deux ressources Terraform.

Vous pourriez facilement échanger Cloudflare contre n'importe quel autre service de noms de domaine de votre choix. Si vous gérez vos domaines via Amazon Route 53 par exemple, vous n'avez pas besoin de passer à Cloudflare pour ce tutoriel. Créez simplement les configurations Terraform appropriées nécessaires pour mapper l'adresse IPv4 de notre instance de serveur à un nom de domaine (il n'est pas non plus obligatoire que ce soit un sous-domaine, vous pouvez mapper un domaine Apex, comme vous le préférez).

Comme prévu (probablement), il s'ensuit également que vous n'avez pas besoin de créer votre serveur sur Digital Ocean, vous pourriez le déployer sur AWS ou tout autre fournisseur de votre choix, à condition que nous obtenions un serveur avec Nginx installé et une adresse IPv4.

Pour rendre les choses plus amusantes, vous pourriez même provisionner un serveur de base et utiliser un outil tel qu'Ansible pour y déployer Nginx ou toute autre application web. Vous pourriez construire votre propre image personnalisée avec des outils comme Packer et l'utiliser également. C'est votre choix. Ce projet se veut rapide et facile à suivre.

1.1 créer providers.tf

Ensuite, nous allons créer un fichier providers.tf pour configurer nos différents fournisseurs. Les contraintes de version des fournisseurs sont définies sur les plus récentes au moment de la rédaction de cet article.

terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "2.34.1"
    }
    cloudflare = {
      source = "cloudflare/cloudflare"
      version = "4.24.0"
    }
  }
}

# Digital Ocean API credentials
provider "digitalocean" {
  token = var.do_token
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

Ce qui précède représente les arguments minimums absolus requis par les différents fournisseurs pour fonctionner correctement. Une fois ce fichier créé, nous allons initialiser Git dans le répertoire de notre projet et faire de ce fichier notre tout premier commit via les commandes suivantes ;

git init .
git add .
git commit -m "Initial commit"

Nous avons transformé les valeurs de nos arguments en variables pour éviter de coder en dur ces valeurs sensibles dans nos fichiers de configuration. N'oubliez pas non plus que vous pouvez personnaliser les messages de commit Git comme bon vous semble.

1.2 créer variables.tf

Puisque nous avons assigné des variables comme valeurs aux arguments dans nos configurations de fournisseurs, il est temps de déclarer ces variables. Nous faisons cela dans un fichier que nous appellerons variables.tf, avec le contenu suivant.

variable "do_token" {
  type = string
}

variable "do_region" {
  type = string
}

variable "cloudflare_api_token" {
  type = string
}

variable cloudflare_zone_id" {
  type = string
}

Nous pouvons maintenant aller de l'avant et commiter ce nouveau fichier dans Git avec un message de commit approprié.

1.3 Initialiser Terraform

Maintenant que nous avons notre configuration de base, il est temps d'exécuter terraform init dans le répertoire de notre projet pour initialiser Terraform.

Terraform Init

Votre message de succès devrait ressembler à celui ci-dessus.

Maintenant, nous devons faire deux choses de plus ;

  • Ajouter un fichier .gitignore adapté à Terraform à notre projet
  • Fournir les valeurs aux différentes variables que nous avons configuré Terraform pour utiliser.

Nous pouvons récupérer un fichier .gitignore adapté à Terraform à l'adresse suivante https://github.com/github/gitignore/blob/main/Terraform.gitignoretext

Il est maintenant temps d'aller de l'avant et de récupérer les différentes clés API requises pour nos fournisseurs depuis leurs plateformes respectives. Nous aurons besoin de ;

  • un Token API avec un accès en write de Digital Ocean, ainsi qu'un code court de région (qui spécifie dans quelle région nous souhaitons déployer nos ressources)
  • un Token API de Cloudflare, ainsi que le Zone ID pour le domaine dans lequel nous souhaitons créer notre enregistrement DNS.

La manière exacte d'obtenir ces informations d'identification ne sera pas couverte ici (vous devriez être en mesure de les acquérir).

Par souci de simplicité, vous pouvez utiliser la région DigitalOcean fra1. Cela correspond au centre de données de Francfort.

Vient maintenant le moment de fournir à Terraform les valeurs que nous avons acquises. Pour être direct, nous allons créer un fichier terraform.tfvars et introduire nos valeurs de variables. Notre fichier devrait ressembler à ce qui suit ;

do_token = "value"
do_region = "fra1"

cloudflare_api_token = "value"
cloudflare_zone_id = "value"

N'OUBLIEZ PAS : Vous ne devez jamais commiter le fichier terraform.tfvars dans Git. Ce fichier contient des secrets d'application importants qui ne doivent être partagés avec personne. Si un acteur malveillant accède à ces secrets, l'ensemble de notre infrastructure risque d'être compromise. Le seul but de ce fichier est pour un usage local. Nous emploierons une méthode plus sécurisée pour fournir ces valeurs à Terraform lorsque nous arriverons à l'environnement CI/CD.

1.4 Créer les Ressources

Il est maintenant temps de créer réellement les ressources pour notre Infrastructure. Nous allons créer deux fichiers : un fichier servers.tf et un fichier dns.tf.

  • Le premier fichier, servers.tf, nous le remplirons avec la configuration des ressources pour créer notre serveur DigitalOcean avec l'image officielle DigitalOcean Nginx. Ainsi (nous créerons une ressource digitalocean_droplet) ;
#create server with nginx image
resource "digitalocean_droplet" "server" {
  name     = "gate"
  size     = "s-1vcpu-1gb"
  image    = "nginx"
  region   = var.do_region
}
  • Le deuxième fichier, dns.tf nous le configurerons comme suit (nous créons une ressource cloudflare_record) ;
# Add a record to the domain
resource "cloudflare_record" "server" {
  zone_id = var.cloudflare_zone_id
  name    = "server"
  value   = digitalocean_droplet.server.ipv4_address
  type    = "A"
  ttl     = 1
  proxied = true
}

Maintenant, enregistrez les deux fichiers et commitez-les dans Git avec des messages de commit appropriés.

À ce stade, l'arborescence de votre répertoire devrait ressembler exactement à ce qui suit ;

Directory tree 1

2. Créer et Initialiser le Backend Distant

Il est temps d'ajouter un backend distant à notre projet. Puisque je suis un Google Cloud Engineer, je travaillerai avec un backend distant gcs dans ce projet. Vous pouvez modifier cela pour n'importe quel autre backend distant de votre choix, comme un backend s3.

J'ai donc procédé à la création d'un Google Cloud Storage Bucket avec le nom terraform-github-workflow. J'ai créé un compte de service sous le même nom et attribué au principal la permission Storage Object Admin sur le bucket gcs. Cela permettra au compte de service de créer et de gérer des objets dans le bucket terraform-github-workflow. Ensuite, j'ai procédé à la création d'une JSON service account key pour le principal du compte de service, que j'ai téléchargée et stockée dans un endroit sûr sur ma station de travail locale.

La dernière étape consistait à définir la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS sur le chemin absolu de la clé JSON téléchargée sur mon PC, comme ceci ;

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/json/key/file

Pour être sécurisé, vous pourriez toujours créer une différente service account key à utiliser lorsque nous arriverons à l'environnement CI/CD.

Enfin, il est temps d'exécuter terraform init pour initialiser ce nouveau backend. Selon le backend que vous avez choisi, vous devriez voir un message de réussite très similaire à celui ci-dessous ;

Initialize Remote Backend

3. Créer des flux de travail Github Actions

Maintenant que notre backend distant Terraform est initialisé, il est temps de créer les scripts Github Actions pour notre pipeline CI/CD. Nous créerons au total 3 flux de travail différents comme suit ;

  • un flux de travail validate qui se déclenche à chaque push vers la branche plan de notre projet. Cela validera toute nouvelle modification de notre base de code à l'aide de la commande terraform validate.
  • un flux de travail plan qui se déclenche à chaque Pull request vers la branche main du projet. Ce flux de travail exécutera à blanc les changements d'infrastructure à venir et utilisera le Github Bot pour faire de ces changements un commentaire sur ladite Pull request.
  • un flux de travail deploy qui se déclenche à chaque push/merge vers la branche main. Cela déploie nos changements d'infrastructure approuvés.

3.1 créer workflows/validate.yml

Nous commencerons par créer un dossier .github/workflows à la racine de notre répertoire de projet. Dans le répertoire workflows, nous créerons un fichier validate.yml. Notre arborescence de répertoire à ce stade devrait ressembler à ceci ;

Tree 2

Voici le contenu du fichier de flux de travail validate ;

name: Validate

on:
    push: 
        branches: [plan]

env:
    GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}      

jobs:
  deploy:
    runs-on: ubuntu-latest
      
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch submodules (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_wrapper: true

      - name: Setup Backend Credentials
        id: backend
        run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

      - name: Terraform Init
        id: init
        run: terraform init

      - name: Terraform Validate
        id: terraform
        run: terraform validate -no-color

Ce flux de travail :

  • se déclenche à chaque push vers la branche plan
  • s'exécute sur l'image latest Ubuntu
  • récupère notre code en tant que première étape
    • configure ensuite Terraform
    • configure les identifiants du backend gcs
    • exécute terraform init &
    • exécute terraform validate sur notre base de code

Maintenant, commitons ce fichier dans Git et passons à la suite.

3.2 créer workflows/deploy.yml

Ensuite, nous créerons notre flux de travail deploy avec le contenu suivant ;

name: Deploy

on:
    push:
        branches: [main]

env:
    GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

jobs:
  deploy:
    runs-on: ubuntu-latest
      
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch submodules (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Setup Backend Credentials
        id: backend
        run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

      - name: Terraform Init
        id: init
        run: terraform init
        
      - name: Terraform Apply
        id: apply
        env:
          TF_VAR_do_token: ${{ secrets.do_token }}
          TF_VAR_do_region: ${{ secrets.do_region }}
          TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
          TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
        run: terraform apply -auto-approve

Ce flux de travail :

  • se déclenche à chaque push/merge vers la branche main
  • s'exécute sur l'image latest Ubuntu
  • récupère notre code en tant que première étape
    • configure ensuite Terraform
    • configure les identifiants du backend gcs
    • exécute terraform init &
    • exécute terraform apply avec -auto-approve pour déployer notre infrastructure

Une fois de plus, commitez ce flux de travail dans Git avec un message approprié et nous créerons notre flux de travail final.

3.3 créer workflows/plan.yml

Le dernier (mais non le moindre) flux de travail que nous allons créer est le flux de travail plan. Nous créons ce flux de travail en dernier car c'est en lui que nous intègrerons notre Github Bot pour nous aider à fournir les résultats de nos tests à blanc afin que les examens de nos Pull-requests soient plus faciles et plus pertinents.

Nous allons donc d'abord créer le flux de travail avec les fonctionnalités nécessaires, puis intégrer le Bot à l'étape suivante. Voici le code pour notre flux de travail plan ;

name: Plan

on:
    pull_request: 
        branches: [main]

env:
  GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      pull-requests: write
      
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch submodules (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_wrapper: true

      - name: Setup Backend Credentials
        id: backend
        run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

      - name: Terraform Init
        id: init
        run: terraform init

      - name: Terraform fmt
        id: fmt
        run: terraform fmt -check
        continue-on-error: true

      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color
        
      - name: Terraform Plan
        id: plan
        env:
            TF_VAR_do_token: ${{ secrets.do_token }}
            TF_VAR_do_region: ${{ secrets.do_region }}
            TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
            TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
        run: terraform plan -no-color

Ce flux de travail :

  • se déclenche à chaque pull-request vers la branche main
  • s'exécute sur l'image latest Ubuntu
  • acquiert la permission d'écrire sur les Pull requests
  • récupère notre code en tant que première étape
    • configure ensuite Terraform
    • configure les identifiants du backend gcs
    • exécute terraform init
    • exécute terraform fmt
    • exécute terraform validate (à nouveau)
    • exécute terraform plan avec un test à blanc de notre infrastructure

Parfait ! Nous pouvons maintenant aller de l'avant et ajouter le Bot dans la section suivante. N'oubliez pas de commiter votre nouveau flux de travail plan dans Git.

4. Configurer le Github Bot

Ajouter le Github Bot à notre flux de travail est assez simple. Il s'agit juste d'une étape supplémentaire sur notre flux de travail plan.

Ajoutez l'étape suivante à votre fichier de flux de travail plan.yml :

      - name: Comment plan on PR
        uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
                // 1. Retrieve existing bot comments for the PR
                const { data: comments } = await github.rest.issues.listComments({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                })
                const botComment = comments.find(comment => {
                return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
                })

                // 2. Prepare format of the comment
                const output = ` ## Terraform Results
                #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
                #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
                #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
                <details><summary>Validation Output</summary>

                \`\`\`\n
                ${{ steps.validate.outputs.stdout }}
                \`\`\`

                </details>

                #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`

                <details><summary>Show Plan</summary>

                \`\`\`\n
                ${process.env.PLAN}
                \`\`\`

                </details>

                *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;

                // 3. If we have a comment, update it, otherwise create a new one
                if (botComment) {
                github.rest.issues.updateComment({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    comment_id: botComment.id,
                    body: output
                })
                } else {
                github.rest.issues.createComment({
                    issue_number: context.issue.number,
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    body: output
                })
                }

Votre fichier plan.yml en entier devrait maintenant ressembler exactement à ceci :

name: Plan

on:
    pull_request: 
        branches: [main]

env:
  GOOGLE_APPLICATION_CREDENTIALS: ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      pull-requests: write
      
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch submodules (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_wrapper: true

      - name: Setup Backend Credentials
        id: backend
        run: echo '${{ secrets.GCS_KEY }}' > ${{ vars.GOOGLE_APPLICATION_CREDENTIALS }}

      - name: Terraform Init
        id: init
        run: terraform init

      - name: Terraform fmt
        id: fmt
        run: terraform fmt -check
        continue-on-error: true

      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color
        
      - name: Terraform Plan
        id: plan
        env:
            TF_VAR_do_token: ${{ secrets.do_token }}
            TF_VAR_do_region: ${{ secrets.do_region }}
            TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
            TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
        run: terraform plan -no-color

      - name: Comment plan on PR
        uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
                // 1. Retrieve existing bot comments for the PR
                const { data: comments } = await github.rest.issues.listComments({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                })
                const botComment = comments.find(comment => {
                return comment.user.type === 'Bot' && comment.body.includes('Terraform Format and Style')
                })

                // 2. Prepare format of the comment
                const output = ` ## Terraform Results
                #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
                #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
                #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
                <details><summary>Validation Output</summary>

                \`\`\`\n
                ${{ steps.validate.outputs.stdout }}
                \`\`\`

                </details>

                #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`

                <details><summary>Show Plan</summary>

                \`\`\`\n
                ${process.env.PLAN}
                \`\`\`

                </details>

                *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;

                // 3. If we have a comment, update it, otherwise create a new one
                if (botComment) {
                github.rest.issues.updateComment({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    comment_id: botComment.id,
                    body: output
                })
                } else {
                github.rest.issues.createComment({
                    issue_number: context.issue.number,
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    body: output
                })
                }

5. Pousser la base de code vers Github

Une fois que vous avez terminé de commiter les nouveaux ajouts ci-dessus dans Git, il est temps de publier (publish) notre base de code d'infrastructure sur Github.

Maintenant, rendez-vous simplement sur Github et créez un dépôt public (ou privé), ajoutez le dépôt comme remote origin pour le projet sur notre machine locale, et poussez (push).

Immédiatement après avoir fait cela, votre flux de travail deploy devrait commencer à s'exécuter, mais ne vous inquiétez pas car il va échouer. Il échouera parce que l'environnement Github Actions n'a pas été configuré avec les identifiants appropriés pour à la fois Initialiser notre Backend Terraform et fournir les valeurs appropriées aux variables définies dans notre infrastructure.

The very first Deploy workflow fails
Failed Deploy workflow details

Il est maintenant temps de configurer correctement l'environnement CI/CD de Github Actions avec les identifiants d'accès appropriés pour déployer notre charge de travail.

6. Configurer Github Environments pour le Backend Terraform et les Variables

Maintenant, nous devons nous rendre dans Github Environments et configurer les secrets et les env vars nécessaires au succès de notre flux de travail.

Si vous l'avez remarqué tout au long de nos flux de travail Github Actions, pour chaque opération qui affecte l'état de Terraform, nous avons inclus les différentes variables d'environnement nécessaires à Terraform pour s'exécuter. Une instance de ceci se trouve au niveau de la commande terraform apply dans le flux de travail deploy. L'extrait de code est le suivant ;

      - name: Terraform Apply
        id: apply
        env:
          TF_VAR_do_token: ${{ secrets.do_token }}
          TF_VAR_do_region: ${{ secrets.do_region }}
          TF_VAR_cloudflare_api_token: ${{ secrets.cloudflare_api_token }}
          TF_VAR_cloudflare_zone_id: ${{ secrets.cloudflare_zone_id }}
        run: terraform apply -auto-approve

Voici donc ci-dessous une liste de tous les différents secrets et env vars que nous devons définir (vous verrez que tous ont été intégrés dans les différents flux de travail aux endroits appropriés) ;

  • Variables Terraform
    • TF_VAR_do_token
    • TF_VAR_do_region
    • TF_VAR_cloudflare_api_token
    • TF_VAR_cloudflare_zone_id
  • Secrets et variables du Backend Distant
    • GCS_KEY - La clé Google Cloud Storage.
    • GOOGLE_APPLICATION_CREDENTIALS - Chemin vers la clé Google Cloud Storage dans le système de fichiers

N'oubliez pas de créer une différente GCS Key pour l'environnement Github Actions, et de désactiver celle que nous avons utilisée pour Initialiser Terraform localement, à partir de maintenant toute nouvelle modification de l'infrastructure sera traitée via notre flux de travail. Vous ne devriez pas utiliser votre clé, même pour le déploiement initial des ressources.

Très bien, rendez-vous maintenant sur votre page Github Dashboard -> Settings -> Secrets and Variables -> Actions. C'est la page illustrée ci-dessous.

Create secrets and variables.

Ensuite, nous allons procéder et remplir les valeurs pour les différents secrets et variables d'environnement.

En raison du fait que nous ne travaillons qu'avec un seul environnement dans ce projet, nous allons les remplir en tant que secrets et variables de Référentiel. Dans une configuration plus avancée où nous aurions des environnements dev, stage, qa, prod, nous utiliserions la solution principale Github Environments. Ce projet n'est pas aussi avancé, et nous allons donc nous en tenir aux secrets et variables du Repo.

  1. En dehors de la variable GOOGLE_APPLICATION_CREDENTIALS, tout le reste devrait être un secret. Utilisez une valeur de ./key.json pour cette variable, pour rester simple.

  2. Pour basculer entre les secrets et les variables, utilisez les onglets sous les mêmes noms sur la page.

Lorsque tout est réglé, nous devrions avoir des pages qui ressemblent à ce qui suit :

All our repo secrets set
All our repo variables set

Maintenant, tous nos secrets et variables sont configurés. 🎉 🎉

7. Tester l'ensemble du flux de travail

Enfin, il est temps de mettre cette roue géante en mouvement.

7.1 créer la branche plan

La première étape consistera à créer la branche plan du projet. Nous y avons beaucoup fait référence tout au long des étapes précédentes, il est temps de la créer réellement.

Nous y parviendrons donc avec la commande suivante :

git checkout -b plan

Cela créera la nouvelle branche plan et nous y déplacera dans notre répertoire de travail actuel. Ensuite, nous pousserons cette branche vers Github et la configurerons pour suivre une nouvelle branche origin sous le même nom plan.

git push -u origin plan

Ce push devrait déclencher un flux de travail validate sur Github, si celui-ci réussit, alors nous sommes sur la bonne voie. S'il ne réussit pas, alors vous devez chercher pour découvrir ce que vous avez pu manquer, afin de le corriger. Jetez un œil aux journaux d'erreurs du flux de travail Actions pour comprendre les erreurs.

Cependant, si vous avez suivi cette procédure pas à pas religieusement, vous devriez avoir des écrans de succès similaires à ceux ci-dessous. Votre tout premier flux de travail validate devrait être un succès.

Successfull Validate workflow run
All steps executed successfully

7.2 préparer et créer la toute première PR vers main

Maintenant, pour lancer notre flux de travail, nous devons créer notre première Pull request vers la branche main. À ce stade, ce sera impossible car nos branches main et plan sont synchronisées. Pour que cela fonctionne, nous devons faire un commit factice sur la branche plan.

Pour faire simple, nous allons simplement ajouter un commentaire à l'un de nos fichiers d'Infrastructure, commiter la modification et faire une PR vers main.

Vous pouvez ajouter un commentaire à n'importe quel fichier de votre choix. Je le ferai sur le fichier backend.tf. J'ajouterai les lignes suivantes au début du fichier

# Configure the remote gcs backend

Maintenant, enregistrez votre fichier modifié, commitez-le et poussez-le vers Github. Cela devrait lancer une nouvelle exécution de flux de travail validate sur Github ;

3rd workflow run

Et il devrait réussir.

Success

Nous pouvons maintenant aller de l'avant et créer notre première Pull request vers main. Si tout fonctionne bien, vous devriez obtenir des résultats similaires à ceux que j'ai obtenus ci-dessous.

Plan workflow starts to run
Plan workflow succeeds

Tout devrait être vert dans votre Pull Request maintenant 🎉 🎉

7.3 examiner les résultats du plan via le commentaire du Bot

Maintenant que vos flux de travail se sont exécutés avec succès, vous devriez être en mesure de voir le nouveau commentaire du Bot qui contient des détails très importants sur les modifications d'infrastructure que vous proposez.

Github Bot comments Terraform Plan Output

Allez-y et développez les différentes sections du commentaire pour obtenir plus de détails. En cliquant sur le bouton Show Plan, par exemple, vous verrez le résultat de la commande terraform plan.

Comme vous pouvez le voir, étant donné que nous n'avons que deux ressources définies dans notre configuration, nous voyons un plan pour créer deux ressources.

Plan: 2 to Add

7.4 examiner, itérer sur, et approuver (ou refuser) le plan

À ce stade, en utilisant les règles de Branche natives de Github, un nombre minimum d'examinateurs peut être requis pour examiner les nouvelles modifications d'Infrastructure et les valider. L'équipe peut modifier tout ce dont elle a besoin, les nouveaux commits sur la branche plan s'empileront sous cette pull request, déclenchant une réexécution du plan workflow à chaque fois, s'assurant que l'équipe voit les modifications les plus à jour à apporter lors du prochain apply.

Maintenant, parce que nous voulons voir ce flux de travail jusqu'à la fin et nous assurer que notre Infrastructure est réellement créée, nous allons supposer que l'Équipe adore absolument notre nouvelle Infra et que les responsables l'ont validée. Nous allons donc aller de l'avant et fusionner (Merge) cette Pull Request #1 et faire déployer notre Infra.

7.5 et... c'est Noël

Confirm Merge Pull Request

Je fais un rebasing juste parce que j'aime un historique de commits bien ordonné.

Pull Request #1 Merged

Cela devrait déclencher notre flux de travail deploy dans la branche main, et comme prévu, il devrait fonctionner sans faille, comme ceci ;

Workflow Deployed Successfully
All Deploy Steps Successful

NOUS L'AVONS FAIT !!! 🎉 🎉

Pour confirmer que notre infrastructure est en ligne dans le monde réel, nous pouvons visiter son adresse web. La mienne était server.obi.ninja et elle a fonctionné sans faille.

All Deploy Steps Successful

J'ai https:// car j'ai configuré le service SSL gratuit de Cloudflare. J'ai fait cela en dehors de Terraform, en même temps que la configuration de la zone Cloudflare elle-même, pour garder ce projet et ce tutoriel simples.

8. Notes Supplémentaires

8.1 Règles de Protection de Branche Github

Pour rendre ce projet à toute épreuve afin qu'il fonctionne de manière fiable pour votre organisation, vous devez utiliser les Règles de Protection de Branche Github. Avec ces règles, vous pourrez appliquer des choses importantes telles que ;

  • s'assurer que les branches main et plan ne peuvent pas être supprimées
  • n'autoriser une fusion (merge) vers main qu'après le succès des deux flux de travail validate et plan
  • spécifier le nombre minimum de personnes autorisées à valider tout nouveau changement
  • etc.

8.2 Outils Supplémentaires

Vous souhaiterez peut-être développer les différentes parties de ce projet avec des outils supplémentaires pour améliorer la sécurité. Des outils tels que :

  • Hashicorp Vault pour la gestion des secrets
  • Sonarqube pour une analyse statique et de qualité du code supplémentaire
  • etc.

8.3 Détruire l'Infrastructure

Il s'agit d'un flux de travail d'équipe, et c'est la colonne vertébrale de l'infrastructure de l'entreprise. En conséquence, toute destruction de ressources doit être effectuée en supprimant ou en commentant le code Terraform pour lesdites ressources et en faisant passer cette modification par un examen par l'Équipe responsable. Personne ne sera en mesure de simplement exécuter terraform destroy et de détruire des ressources, tant que les secrets de l'Infrastructure sont gérés correctement.

8.4 Conclusion

Je me suis beaucoup amusé à imaginer, exécuter et documenter ce projet. J'espère que vous le trouverez utile. N'hésitez pas à me faire savoir ce que vous en pensez, toute erreur ou marge d'amélioration dans la section des commentaires ci-dessous.