Création d’un pipeline d’intégration continue et de déploiement continu avec DSC

Azure Pipelines | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018 | TFS 2017

Notes

dans Microsoft Team Foundation Server (TFS) 2018 et versions antérieures, les pipelines de build et de mise en version sont appelés définitions, les exécutions sont appelées builds, les connexions de service sont appelées points de terminaison de service, les étapes sont appelées environnements et les tâches sont appelées phases.

Cet exemple montre comment créer un pipeline d’intégration continue/de déploiement continu (CI/CD) à l’aide de PowerShell, DSC et pester.

Une fois le pipeline est créé et configuré, vous pouvez l’utiliser pour totalement déployer, configurer et tester un serveur DNS et ses enregistrements d’hôte associés. Ce processus simule la première partie d’un pipeline qui sera utilisée dans un environnement de développement.

Un pipeline CI/CD automatisé vous permet de mettre à jour les logiciels avec plus de rapidité et de fiabilité, garantissant que tout le code est testé et qu’une version actuelle de votre code est disponible à tout moment.

Prérequis

Pour utiliser cet exemple, vous devez maîtriser les éléments suivants :

Ce dont vous aurez besoin

Pour générer et exécuter cet exemple, vous aurez besoin d’un environnement comportant plusieurs ordinateurs et/ou machines virtuelles.

Client

Il s’agit de l’ordinateur sur lequel vous allez effectuer toutes les tâches pour configurer et exécuter l’exemple. L’ordinateur client doit être un ordinateur Windows avec les éléments suivants :

Azure DevOps Récurrent

Une organisation Azure DevOps. Si vous n’en avez pas, vous pouvez en créer un gratuitement. (une organisation Azure DevOps est différente de votre organisation GitHub. Donnez-leur le même nom si vous souhaitez les aligner entre eux.)

TFSSrv

L’ordinateur qui héberge le serveur TFS sur lequel vous allez définir votre build et votre version. Team Foundation Server 2017 doit être installé sur cet ordinateur.

BuildAgent

L’ordinateur qui exécute l’agent de build Windows qui crée le projet. Un agent de build doit être installé et en cours d’exécution sur cet ordinateur. Consultez Déployer un agent sur Windows pour savoir comment installer et exécuter un agent de build Windows.

Vous devez également installer les modules DSC xDnsServer et xNetworking sur cet ordinateur.

TestAgent1

Il s’agit de l’ordinateur configuré comme serveur DNS par la configuration DSC dans cet exemple. L’ordinateur doit exécuter Windows Server 2016.

TestAgent2

Il s’agit de l’ordinateur qui héberge le site Web que cet exemple configure. L’ordinateur doit exécuter Windows Server 2016.

Ajouter le code à un référentiel

Nous commençons par créer un référentiel git, puis nous importons le code à partir de votre référentiel local sur l’ordinateur client. Si vous n’avez pas déjà cloné le référentiel Demo_CI sur votre ordinateur client, faites-le maintenant en exécutant la commande git suivante :

git clone https://github.com/PowerShell/Demo_CI
  1. Sur votre ordinateur client, accédez à votre serveur TFS dans un navigateur web.

  2. Créez un projet d’équipe nommé Demo_CI.

    Assurez-vous que l’option Contrôle de version est définie sur Git.

  3. Sur votre ordinateur client, ajoutez un accès à distance au référentiel que vous venez de créer dans TFS avec la commande suivante :

    git remote add tfs <YourTFSRepoURL>

    <YourTFSRepoURL> est le clone de l’URL dans le référentiel TFS que vous avez créé à l’étape précédente.

    Si vous ne savez pas où trouver cette URL, consultez Cloner un référentiel Git existant.

  4. Transmettez le code de votre référentiel local à votre référentiel TFS avec la commande suivante :

    git push tfs --all

  5. Le référentiel TFS contiendra le code Demo_CI.

  1. accédez à votre abonnement Azure DevOps dans un navigateur web.

  2. Créez un projet d’équipe nommé Demo_CI. Assurez-vous que l’option Contrôle de version est définie sur Git.

  3. Sur votre ordinateur client, ajoutez un serveur distant au référentiel que vous venez de créer à l’aide de la commande suivante :

    git remote add devops <YourDevOpsRepoURL>

    <YourDevOpsRepoURL> est l’URL de clonage du référentiel Azure DevOps que vous avez créé à l’étape précédente.

    Si vous ne savez pas où trouver cette URL, consultez Cloner un référentiel Git existant.

  4. Transmettez le code de votre référentiel local à votre référentiel TFS avec la commande suivante :

    git push devops --all

  5. le référentiel Azure DevOps sera rempli avec le code Demo_CI.

Notes

Cet exemple utilise le code de la branche ci-cd-example du dépôt Git. Veillez à spécifier cette branche en tant que branche par défaut dans votre projet, et pour les déclencheurs CI/CD que vous créez.

Présentation du code

Avant de créer les pipelines de build et de déploiement, examinons le code pour comprendre ce qui se passe. Sur votre ordinateur client, ouvrez votre éditeur de texte favori et accédez à la racine de votre référentiel Git Demo_CI.

La configuration DSC

Ouvrez le fichier DNSServer.ps1 (depuis la racine du référentiel local Demo_CI, ./InfraDNS/Configs/DNSServer.ps1).

Ce fichier contient la configuration DSC qui configure le serveur DNS. Le voici dans son intégralité :

configuration DNSServer
{
    Import-DscResource -module 'xDnsServer','xNetworking', 'PSDesiredStateConfiguration'

    Node $AllNodes.Where{$_.Role -eq 'DNSServer'}.NodeName
    {
        WindowsFeature DNS
        {
            Ensure  = 'Present'
            Name    = 'DNS'
        }

        xDnsServerPrimaryZone $Node.zone
        {
            Ensure    = 'Present'
            Name      = $Node.Zone
            DependsOn = '[WindowsFeature]DNS'
        }

        foreach ($ARec in $Node.ARecords.keys) {
            xDnsRecord $ARec
            {
                Ensure    = 'Present'
                Name      = $ARec
                Zone      = $Node.Zone
                Type      = 'ARecord'
                Target    = $Node.ARecords[$ARec]
                DependsOn = '[WindowsFeature]DNS'
            }
        }

        foreach ($CName in $Node.CNameRecords.keys) {
            xDnsRecord $CName
            {
                Ensure    = 'Present'
                Name      = $CName
                Zone      = $Node.Zone
                Type      = 'CName'
                Target    = $Node.CNameRecords[$CName]
                DependsOn = '[WindowsFeature]DNS'
            }
        }
    }
}

Notez l’instruction Node :

Node $AllNodes.Where{$_.Role -eq 'DNSServer'}.NodeName

Elle recherche tous les nœuds qui ont été définis comme ayant un rôle de DNSServer dans les données de configuration, créées par le script DevEnv.ps1.

Vous pouvez en savoir plus sur la méthode Where dans about_arrays

Il est important d’utiliser les données de configuration pour définir des nœuds lors de l’opération CI car les informations sur les nœuds sont susceptibles de changer entre les environnements, et ces données de configuration vous permet de modifier facilement les informations des nœuds sans modifier le code de la configuration.

Dans le premier bloc de ressources, la configuration appelle WindowsFeature pour s’assurer que la fonctionnalité DNS est activée. Les blocs de ressources qui suivent appellent des ressources à partir du module xDnsServer pour configurer la zone principale et les enregistrements DNS.

Notez que les deux blocs xDnsRecord sont encapsulés dans des boucles foreach qui forment une itération via des tableaux dans les données de configuration. Là encore, les données de configuration sont créées par le script DevEnv.ps1, ce que allons examiner maintenant.

Données de configuration

Le fichier DevEnv.ps1 (depuis la racine du référentiel local Demo_CI, ./InfraDNS/DevEnv.ps1) spécifie les données de configuration spécifiques à l’environnement dans une table de hachage, puis passe cette table de hachage à un appel à la fonction New-DscConfigurationDataDocument, définie dans DscPipelineTools.psm (./Assets/DscPipelineTools/DscPipelineTools.psm1).

Le fichier DevEnv.ps1 :

param(
    [parameter(Mandatory=$true)]
    [string]
    $OutputPath
)

Import-Module $PSScriptRoot\..\Assets\DscPipelineTools\DscPipelineTools.psd1 -Force

# Define Unit Test Environment
$DevEnvironment = @{
    Name                        = 'DevEnv';
    Roles = @(
        @{  Role                = 'DNSServer';
            VMName              = 'TestAgent1';
            Zone                = 'Contoso.com';
            ARecords            = @{'TFSSrv1'= '10.0.0.10';'Client'='10.0.0.15';'BuildAgent'='10.0.0.30';'TestAgent1'='10.0.0.40';'TestAgent2'='10.0.0.50'};
            CNameRecords        = @{'DNS' = 'TestAgent1.contoso.com'};
        }
    )
}

return New-DscConfigurationDataDocument -RawEnvData $DevEnvironment -OutputPath $OutputPath

La fonction New-DscConfigurationDataDocument (définie dans \Assets\DscPipelineTools\DscPipelineTools.psm1) crée par programmation un document de données de configuration à partir de la table de hachage (données du nœud) et un tableau (données non-nœud), qui sont transmis comme les paramètres RawEnvData et OtherEnvData.

Dans notre cas, seul le paramètre RawEnvData est utilisé.

Le script de build psake

Le script de build psake défini dans Build.ps1 (depuis la racine du référentiel Demo_CI, ./InfraDNS/Build.ps1) définit les tâches qui font partie de la build. Il définit également les autres tâches dont dépend chaque tâche. Lorsqu’il est appelé, le script psake garantit que la tâche spécifiée (ou la tâche nommée Default si aucune tâche n’est spécifiée) s’exécute et que toutes les dépendances s’exécutent également (cette opération est récursive afin que les dépendances de dépendances s’exécutent, et ainsi de suite).

Dans cet exemple, la tâche Default est définie ainsi :

Task Default -depends UnitTests

La tâche Default n’a aucune implémentation elle-même, mais a une dépendance sur la tâche CompileConfigs. La chaîne de dépendances de tâches qui en résulte garantit l’exécution de toutes les tâches figurant dans le script de build.

Dans cet exemple, le script psake est appelé par un appel à Invoke-PSake dans le fichier Initiate.ps1 (situé à la racine du référentiel Demo_CI) :

param(
    [parameter()]
    [ValidateSet('Build','Deploy')]
    [string]
    $fileName
)

#$Error.Clear()

Invoke-PSake $PSScriptRoot\InfraDNS\$fileName.ps1

<#if($Error.count)
{
    Throw "$fileName script failed. Check logs for failure details."
}
#>

Lorsque nous créons la définition de build pour notre exemple, nous fournissons notre fichier de script psake comme fileName paramètre pour ce script.

Le script de build définit les tâches suivantes :

GenerateEnvironmentFiles

Exécute DevEnv.ps1, qui génère le fichier de données de configuration.

InstallModules

Installe les modules requis par la configuration DNSServer.ps1.

ScriptAnalysis

Appelle PSScriptAnalyzer.

UnitTests

Exécute les tests unitaires Pester.

CompileConfigs

Compile la configuration (DNSServer.ps1) dans un fichier MOF, en utilisant les données de configuration générées par la tâche GenerateEnvironmentFiles.

Clean

Crée les dossiers utilisés pour l’exemple, puis supprime les résultats des tests, les fichiers de données de configuration et les modules des exécutions précédentes.

Le script de déploiement psake

Le script de déploiement psake défini dans Deploy.ps1 (depuis la racine du référentiel Demo_CI, ./InfraDNS/Deploy.ps1) définit les tâches qui déploient et exécutent la configuration.

Deploy.ps1 définit les tâches suivantes :

DeployModules

Démarre une session PowerShell sur TestAgent1 et installe les modules contenant les ressources DSC requises pour la configuration.

DeployConfigs

Appelle l’applet de commande Start-DscConfiguration pour exécuter la configuration sur TestAgent1.

IntegrationTests

Exécute les tests d’intégration Pester.

AcceptanceTests

Exécute les tests d’acceptation Pester.

Clean

Supprime tous les modules installés lors des exécutions précédentes et garantit l’existence du dossier de résultats de test.

Scripts de tests

Les tests d’acceptation, d’intégration et unitaires sont définis dans les scripts du dossier Tests (depuis la racine du référentiel Demo_CI, ./InfraDNS/Tests), puis placés dans des fichiers nommés DNSServer.tests.ps1 dans leurs dossiers respectifs.

Les scripts de test utilisent la syntaxe Pester et PoshSpec.

Tests unitaires

Les tests unitaires testent les configurations DSC elles-mêmes pour s’assurer que les configurations exécuteront les tâches prévues. Le script de test unitaire utilise la syntaxe Pester.

Tests d’intégration

Les tests d’intégration testent la configuration du système pour s’assurer, le système est configuré comme prévu lors de son intégration avec d’autres composants. Ces tests sont exécutés sur le nœud cible après que ce dernier a été configuré avec DSC. Le script de test d’intégration utilise une combinaison des syntaxes Pester et PoshSpec.

Tests d’acceptation

Les tests d’acceptation testent le système pour s’assurer qu’il se comporte comme prévu. Par exemple, ils vérifient qu'une page web renvoie les informations appropriées lorsqu’elle est interrogée. Ces tests sont exécutés à distance à partir du nœud cible afin de tester des scénarios en conditions réelles. Le script de test d’intégration utilise une combinaison des syntaxes Pester et PoshSpec.

Définir la build

Maintenant que nous avons chargé notre code sur un référentiel et vu ce qu’il fait, nous allons définir notre Build.

Ici, nous aborderons uniquement les étapes de build que vous ajouterez à la build. pour obtenir des instructions sur la création d’une définition de build dans Azure DevOps, consultez créer et mettre en file d’attente une définition de build.

Créez une nouvelle définition de build (sélectionnez le modèle de pipeline de démarrage ) nommé « InfraDNS ». Ajoutez les étapes suivantes à votre définition de build :

  • PowerShell
  • Publier les résultats des tests
  • Copier les fichiers
  • Publier l’artefact

Après avoir ajouté ces étapes de build, modifiez les propriétés de chaque étape comme suit :

PowerShell

  1. Affectez à la propriété TargetType la valeur File Path .
  2. Affectez à la propriété filePath la valeur initiate.ps1 .
  3. Ajoutez la valeur -fileName build à la propriété Arguments.

Cette étape de la build exécute le fichier initiate.ps1, qui appelle le script de build psake.

Publier les résultats des tests

  1. Affectez à TestResultsFormat la valeur NUnit
  2. Affectez à TestResultsFiles la valeur InfraDNS/Tests/Results/*.xml
  3. Affectez à TestRunTitle la valeur Unit .
  4. Vérifiez que les options de contrôle activées et toujours exécuter sont toutes les deux sélectionnées.

Cette étape de build exécute des tests unitaires dans le script Pester que nous avons examiné précédemment, puis stocke les résultats dans le dossier InfraDNS/Tests/Results/*.xml.

Copier les fichiers

  1. Ajoutez chacune des lignes suivantes à Contents :

    initiate.ps1
    **\deploy.ps1
    **\Acceptance\**
    **\Integration\**
    
  2. Définissez TargetFolder sur $(Build.ArtifactStagingDirectory)\

Cette étape copie la build et les scripts de test dans le répertoire intermédiaire afin de pouvoir publier ces éléments comme des artefacts de build à l’étape suivante.

Publier l’artefact

  1. Affectez à targetPath la valeur $(Build.ArtifactStagingDirectory)\
  2. Affectez à ArtifactName la valeur Deploy
  3. Affectez la valeur à activé true .

Activer l’intégration continue

Nous allons maintenant configurer un déclencheur qui génère le projet chaque fois qu’une modification est apportée à la branche ci-cd-example du référentiel git.

  1. Dans TFS, cliquez sur l’onglet Build et version
  2. Sélectionnez la définition de build DNS Infra, puis cliquez sur Modifier
  3. Cliquez sur l’onglet Déclencheurs
  4. Sélectionnez Continuous integration (CI), puis refs/heads/ci-cd-example dans la liste déroulante de la branche
  5. Cliquez sur Enregistrer, puis sur OK

Désormais, toute modification apportée au référentiel git déclenche une génération automatisée.

Créer la définition de version

Nous allons créer une définition de version afin de déployer le projet dans l’environnement de développement à chaque ajout de code.

Pour ce faire, ajoutez une nouvelle définition de version associée à la définition de build InfraDNS que vous avez créée précédemment. Veillez à sélectionner Continuous deployment afin de déclencher une nouvelle version chaque fois qu’une nouvelle build est terminée. (Que sont les pipelines de mise en production ? ) et configurez-la comme suit :

Ajoutez les étapes suivantes à la définition de version :

  • PowerShell
  • Publier les résultats des tests
  • Publier les résultats des tests

Modifiez les étapes comme suit :

PowerShell

  1. Définissez le champ targetPath sur $(Build.DefinitionName)\Deploy\initiate.ps1"
  2. Définissez le champ Arguments sur -fileName Deploy

Première publication des résultats des tests

  1. SELECT NUnit pour le champ TestResultsFormat
  2. Définissez le champ TestResultsFiles sur $(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Integration*.xml
  3. Affectez à TestRunTitle la valeur Integration
  4. Définir la condition sur succeededOrFailed()

Seconde publication des résultats des tests

  1. SELECT NUnit pour le champ TestResultsFormat
  2. Définissez le champ TestResultsFiles sur $(Build.DefinitionName)\Deploy\InfraDNS\Tests\Results\Acceptance*.xml
  3. Affectez à TestRunTitle la valeur Acceptance
  4. Définir la condition sur succeededOrFailed()

Vérifier les résultats

À présent, chaque fois que vous envoyez des modifications dans la ci-cd-example branche, une nouvelle build démarre. Si la build se termine correctement, un nouveau déploiement est déclenché.

Vous pouvez vérifier le résultat du déploiement en ouvrant un navigateur sur l’ordinateur client, puis en accédant à www.contoso.com.

Étapes suivantes

Cet exemple configure le serveur DNS TestAgent1 afin que l’URL www.contoso.com soit résolue en TestAgent2, mais il ne déploie pas réellement un site Web. Le schéma pour effectuer cette opération est fourni dans le référentiel, sous le dossier WebApp. Vous pouvez utiliser les stubs fournis pour créer des scripts psake, des tests Pester et des configurations DSC afin de déployer votre propre site Web.