Template de pipeline Azure DevOps générique pour Terraform
Le template Terraform
Voici un modèle prêt à l'emploi fournit une approche cohérente et automatisée pour gérer l'infrastructure en tant que code, tout en exploitant les fonctionnalités d'Azure DevOps.
Ce template repose sur les principes de l'intégration continue et du déploiement continu (CI/CD) pour offrir une méthode robuste et fiable de déploiement d'infrastructures Azure. Il permet aux équipes de développement et d'exploitation de collaborer de manière transparente, en automatisant le provisionnement, la configuration et la mise à jour des ressources Azure.
En utilisant les capacités de gestion de version d'Azure DevOps, vous pouvez également suivre et contrôler les changements apportés à votre infrastructure au fil du temps, assurant ainsi la traçabilité et la gouvernance nécessaires.
parameters:
- name: terraformPath
type: string
- name: action
displayName: Action
type: string
default: validate
values:
- validate
- plan
- apply
- destroy
- name: terraformVersion
displayName: Version
type: string
default: '1.5.1'
- name: 'backendServiceArm'
displayName: 'Azure service connection name'
type: string
- name: 'backendAzureRmResourceGroupName'
displayName: 'Ressource group name for the storage account'
type: string
- name: 'backendAzureRmStorageAccountName'
displayName: 'Storage account name'
type: string
- name: 'backendAzureRmContainerName'
displayName: 'Container name'
type: string
default: 'tfstates'
- name: 'backendAzureRmKey'
displayName: 'Terraform state filename'
type: string
jobs:
- job: "Terraform"
displayName: "Terraform ${{ parameters.action}} 🚀"
steps:
- task: TerraformInstaller@0
displayName: "Install Terraform"
inputs:
terraformVersion: ${{ parameters.terraformVersion }}
terraformDownloadLocation: 'https://releases.hashicorp.com/terraform'
- task: TerraformTaskV4@4
displayName: 'Terrafomr init'
inputs:
provider: 'azurerm'
command: 'init'
backendServiceArm: ${{ parameters.backendServiceArm }}
backendAzureRmResourceGroupName: ${{ parameters.backendAzureRmResourceGroupName }}
backendAzureRmStorageAccountName: ${{ parameters.backendAzureRmStorageAccountName }}
backendAzureRmContainerName: ${{ parameters.backendAzureRmContainerName}}
backendAzureRmKey: ${{ parameters.backendAzureRmKey}}
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}
ensureBackend: true
- task: TerraformTaskV4@4
condition: eq('${{ parameters.action }}', 'validate')
displayName: 'Terraform validate'
inputs:
command: validate
- task: TerraformTaskV4@4
condition: |
or(
eq('${{ parameters.action }}', 'apply'),
eq('${{ parameters.action }}', 'plan')
)
name: terraformPlan
displayName: 'Terraform plan'
inputs:
command: plan
commandOptions: "-out=tfplan"
environmentServiceNameAzureRM: ${{ parameters.backendServiceArm }}
publishPlanResults: 'Terraform Plan'
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}
- task: TerraformTaskV4@4
condition: |
or(
eq('${{ parameters.action }}', 'apply'),
eq('${{ parameters.action }}', 'plan')
)
displayName: 'Terraform show'
inputs:
provider: 'azurerm'
command: 'show'
commandOptions: '$(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}/tfplan'
outputTo: 'file'
outputFormat: 'json'
fileName: '$(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}/tfplan.json'
environmentServiceNameAzureRM: ${{ parameters.backendServiceArm }}
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}
- task: PublishPipelineArtifact@1
condition: |
or(
eq('${{ parameters.action }}', 'apply'),
eq('${{ parameters.action }}', 'plan')
)
inputs:
targetPath: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}/tfplan.json
artifact: 'Plan'
publishLocation: 'pipeline'
- task: TerraformTaskV4@4
condition: eq('${{ parameters.action }}', 'apply')
displayName: 'Terraform apply'
inputs:
command: apply
commandOptions: "tfplan"
condition: eq(variables['terraformPlan.changesPresent'], 'true')
environmentServiceNameAzureRM: ${{ parameters.backendServiceArm }}
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}
- task: TerraformTaskV4@4
condition: eq('${{ parameters.action }}', 'destroy')
displayName: 'Terraform destroy'
inputs:
command: destroy
environmentServiceNameAzureRM: ${{ parameters.backendServiceArm }}
workingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.terraformPath}}
Comment l'exploiter
Voici un exemple simple pour appeler le template précédent depuis un pipeline Azure DevOps. Dans ma solution, j'ai positionné le template dans une arborescence "./templates/terraform.yaml" et mes scripts d'IaC Terraform sont présents dans le répertoire "./terraform".
name: Deploy IaC $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)
trigger:
branches:
include:
- main
- feature/*
paths:
exclude:
- pipelines/*
pool:
vmImage: ubuntu-latest # This is the default if you don't specify a pool or vmImage.
variables:
- name: TFSource-Path
value: "./terraform/"
stages:
- stage: "Apply"
displayName: "DEV - Deploy Infrasctruture using Terraform ✅"
jobs:
- template: templates/terraform.yaml
parameters:
action: apply
terraformPath: $(TFSource-Path)
backendServiceArm: 'Dev-Demo-Azdotemplate'
backendAzureRmResourceGroupName: 'rg-demo-azdotemplate'
backendAzureRmStorageAccountName: 'sastatesdemoazdotemplatetf'
backendAzureRmContainerName: 'tfstate'
backendAzureRmKey: 'demoazdotemplate.dev.tfstate'
Cette solution me permet d'enchainer mes différents environnements en exécutant le même modèle de déploiement et même me permet de faire appel à d'autre processus.
Par exemple:
Je peux ajouter une brique SAST (Checkov) dont voici le template :
parameters:
- name: terraformPath
type: string
jobs:
- job: "Checkov"
displayName: "Checkov > Pull, run and publish results of Checkov scan"
steps:
- bash: |
docker pull bridgecrew/checkov
docker run --volume $(pwd):/tf bridgecrew/checkov --directory /tf --output junitxml --soft-fail > $(pwd)/Checkov-Report.xml
workingDirectory: '${{ parameters.terraformPath }}'
failOnStderr: false
displayName: "Pull & Run > bridgecrew/checkov"
name: CheckovScan
condition: always()
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "Checkov-Report.xml"
searchFolder: "${{ parameters.terraformPath }}"
testRunTitle: "Checkov Results"
mergeTestResults: false
failTaskOnFailedTests: false
publishRunAttachments: true
displayName: "Publish > Checkov scan results"
# Clean up any of the containers / images that were used for quality checks
- bash: |
docker rmi "bridgecrew/checkov:latest" -f | true
displayName: 'Clean > Remove Checkvov docker image'
condition: always()
Que je référence en amont de mon déploiement terraform dans mon pipeline
name: Deploy IaC $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)
trigger:
branches:
include:
- main
- feature/*
paths:
exclude:
- pipelines/*
pool:
vmImage: ubuntu-latest # This is the default if you don't specify a pool or vmImage.
variables:
- name: TFSource-Path
value: "./terraform/"
stages:
- stage: "SAST"
displayName: "Static code analysis using Checkov 👮♀️"
jobs:
- template: templates/checkov.yaml
parameters:
terraformPath: $(TFSource-Path)
- stage: "Apply"
displayName: "DEV - Deploy Infrasctruture using Terraform ✅"
jobs:
- template: templates/terraform.yaml
parameters:
action: apply
terraformPath: $(TFSource-Path)
backendServiceArm: 'Dev-Demo-Azdotemplate'
backendAzureRmResourceGroupName: 'rg-demo-azdotemplate'
backendAzureRmStorageAccountName: 'sastatesdemoazdotemplatetf'
backendAzureRmContainerName: 'tfstate'
backendAzureRmKey: 'demoazdotemplate.dev.tfstate'
J'espére que cet article vous a fourni des informations précieuses et vous a aidé à mieux comprendre l'importance d'une approche automatisée et cohérente pour le déploiement d'infrastructures sur Azure. Je suis convaincus que l'utilisation de ce template de pipeline facilitera vos processus de déploiement, tout en vous offrant une plus grande agilité et une meilleure gouvernance.
Si vous avez des questions supplémentaires ou si vous souhaitez explorer davantage le sujet, n'hésitez pas à me contacter via linkedIn ou en laissant un commentaire.
Subscribe to my newsletter
Read articles from Antoine LOIZEAU directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Antoine LOIZEAU
Antoine LOIZEAU
Ayant eu de multiples expériences dans le monde du conseil, j'ai pu acquérir une expertise dans la conception et la construction de services de collaboration d'entreprise. Je suis passionné par les technologies Microsoft et surtout Azure. Aujourd'hui je possède une solide expérience en méthodologie de développement et j'ai mené des équipes de développement technique au succès. Je possède également une solide connaissance de l'infrastructure qui fait de moi une ressource efficace pour mettre en œuvre la transformation numérique vers le cloud Microsoft. Je suis un professionnel efficace et honnête qui aime relever les challenges. Aimant partager mes connaissances; je suis à l'aise en tant que Technical Leader et en tant que membre d'une équipe. Mes compétences techniques sont les suivantes: Azure, DevOps, Architecture Applicative, Développement de solution Cloud Native, écosystème Microsoft... et bien d'autres. Pour voir mes certifications Microsoft : https://www.youracclaim.com/users/antoine-loizeau