Que cela soit pour mettre en place des infras sur Azure, des scripts ou des modules ou mêmes des fichiers bat, l’admin aujourd’hui est obligé de stocker et de gérer du code. Git c’est largement imposé dans le monde des développeurs pour la gestion non seulement du code source mais aussi pour la gestion du projet. Le monde des OPS reste encore largement hermétique à cela.

Souvent les scripts sont encore stockés sur un partage de fichiers lorsqu’ils doivent être partagé. Au mieux la version finale est placée dans un git sans pour autant que l’on utilise toutes les possibilités de l’outils. Il est fréquent de voir des personnes avancées n’utiliser que quelques commandes sur Git.

Ce post se propose de revenir sur les concepts utilisés dans Git et de les appliquer à la collaboration entre Admin sur des projets de scripts, de module PS ou d’infra as code.

Commençons ! Dans un répertoire, on ajoute 5 dossiers, les quatre premiers correspondent à quatre admins et le dernier deviendra un repository centrale.

mkdir olivier,lucien,maurice,alexandre,remote 

On rentre dans le répertoire olivier et on y ajoute une configuration git.

cd olivier 
git init

Ce que nous venons de faire, c’est de créer l’espace de travail d’Olivier, le dossier olivier, c’est ici que l’on mettra nos fichiers et nos scripts.
La command git init, initialise le repository. Dans l’explorer Windows, en affichant les fichiers et les dossiers cachés, vous devriez voir un répertoire nommé .git. Ce répertoire sert à la configuration de Git est stocke deux éléments.

  • Le staging area
  • Le local repository

Le premier peut être vus comme un entrepôt provisoire pour les donner que l’on voudrait ajouter à un Commit . Le Second permet de stocker les diffèrent commits et donc l’historique de notre code.

Créons le fichier dsc\web.dsc.ps1

new-item -name dsc -ItemType Directory 
new-item -path "dsc\web.dsc.ps1" -ItemType file 

Nous avons créé un premier script dans notre espace de travail

Voyons ce qu’il se passe pour git. Pour cela il faut utiliser git status

git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        dsc/

nothing added to commit but untracked files present (use "git add" to track)

Cela indique que git voit un ou plusieurs fichiers, qui ne sont ni en staging ni dans le repository local.

Pour pouvoir le mettre en staging

Git add dsc\web.dsc.ps1 

Git status  
On branch master 

 No commits yet 

 Changes to be committed: 

  (use "git rm --cached <file>..." to unstage) 

        new file:   dsc/web.dsc.ps1 

Le fichier est maintenant dans l’espace de staging

Si l’on modifie le fichier pour integrer une configuration.

Configuration iisdeploy {
	Import-DscResource -Module xPSDesiredStateConfiguration

	Node localhost {

		WindowsFeature IIS
		{		
    			Ensure = 'Present'
    			Name = 'Web-Server'
		}

	}
}
git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   dsc/web.dsc.ps1

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   dsc/web.dsc.ps1

La création du fichier est bien enregistrée dans le staging area mais l’ajout du code non.

On peut s’en rendre compte en utilisant un git diff

git diff dsc\web.dsc.ps1
diff --git a/dsc/web.dsc.ps1 b/dsc/web.dsc.ps1
index bc2a057..adf793d 100644
--- a/dsc/web.dsc.ps1
+++ b/dsc/web.dsc.ps1
@@ -1 +1,14 @@
\ No newline at end of file
+Configuration iisdeploy {
+       Import-DscResource -Module xPSDesiredStateConfiguration
+       Import-DscResource -Module xWebAdministration
+
+       Node localhost {
+
+               WindowsFeature IIS
+               {
+                       Ensure = 'Present'
+                       Name = 'Web-Server'
+               }
+
+       }
+}
\ No newline at end of file

On voit bien l’ajout des lignes de code symbolisé par le signe + et la couleur verte. Les suppressions sont-elles symbolisées par un signe – et une couleur rouge

new-item -path "dsc\README.md " -ItemType file

Et un texte explicatif

Set-Content -Path dsc\README.md -Value "# DSC Configuration"

Nous avons une première étape de travail, l’ajout d’un squelette de configuration DSC.

Nous pouvons faire un commit. Un commit permet d’inclure les éléments que nous avons en staging dans le repository local.

git commit -m "Add DSC Folder and web config" 
[master (root-commit) ef8cf6b] Add DSC Folder and web config
 1 file changed, 1 insertion(+)
 create mode 100644 dsc/web.dsc.ps1

Plusieurs informations ici. Notre commit ne concerne que les éléments en staging. Le fichier README.md et les modifications n’ont pas été pris en compte. Nous devons faire l’ajout des modifications

Pour ce faire nous pouvons utiliser git add –all mais attention c’est à double tranchant car dans ce cas toutes les modifications sont enregistrées y compris celles qui n’ont pas de rapport entre elle.

git add –-all
git commit -m "Add DSC Folder and web config" 
[master 89c5c6d] Add DSC Folder and web config
 2 files changed, 15 insertions(+), 1 deletion(-)
 create mode 100644 dsc/README.md

Autre point nous avons ajouter un message avec -m. Ce message n’est pas obligatoire, mais il permet d’indiquer la raison du commit, quel changement il contient. Et comme on vient de la voir, le message peut être le même sur plusieurs commit.

Si l’on fait un git log

git log
commit 89c5c6d9cf024051fc52b6c926800188e4b02ba5 (HEAD -> master)
Author: Olivier Miossec <oliviermiossec@hotmail.fr>
Date:   Sun Jun 16 16:04:58 2019 +0200

    Add DSC Folder and web config

commit ef8cf6bdd8b99709d6fe6bff8f80efce2dd68500
Author: Olivier Miossec <oliviermiossec@hotmail.fr>
Date:   Sun Jun 16 15:59:50 2019 +0200

    Add DSC Folder and web config

On retrouve la liste de nos commits, avec leur ID et leur message (d’où leur importance) et l’on trouve aussi l’information (HEAD -> master).

HEAD est par contre un marqueur qui indique où se trouve, dans quelle branche et sur quel commit, se situe le repository.

Nous venons donc de voir les différences entre espace de travail, espace de staging et repository local. Nous avons aussi une notion de ce qu’est HEAD et de la branche principale nommée master.

Nous venons aussi de voir qu’un Commit correspond à un ensemble de fichiers et de modification qui dans l’idéal correspondent à une étape d’un projet.

Git est un outil de collaboration. Cela veut dire qu’en plus d’avoir un repository local, celui que nous venons de manipuler, il est possible d’avoir un repository en ligne où peuvent travailler plusieurs personnes.

Le repository dans le dossier olivier, ne dispose pas de repository remote Git remote –v ne renvois rien et avec

git remote get-url origin 

On obtient l’erreur : fatal: No such remote ‘origin’

Origin peut être compris ici comme un synonyme de remote, du repository original.

C’est normal car nous avons uniquement créer un repository local

Pour créer un remote repository, on se déplace dans le répertoire remote créer lors de la première étape. On y ajoute alors une configuration git spécifique.

git init --bare TeamRemoteWork.git

La commande configure un nouveau repository mais si l’on regarde à l’intérieur de ce nouveau dossier on remarque qu’il ne comprend les données git.

Si l’on revient dans le dossier olivier, on peut maintenant ajouter un remote

git remote add origin C:\work\psworkshop\git\remote\TeamRemoteWork.git

La commande git remote –v nous renvois alors les informations

origin  C:\work\psworkshop\git\remote\TeamRemoteWork.git (fetch)
origin  C:\work\psworkshop\git\remote\TeamRemoteWork.git (push)

La configuration comprend en plus de l’espace de travail, de l’espace de staging et du repository local, un remote repository.

Ce dernier est le point où l’ensemble d’une equipe partage leur commits. Git remove -v renvois deux endpoint disponible suivit de deux opérations Fetch et Push.

Push est l’action d’envoyer les données du repository local vers le repository en remote.

git push origin master
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (9/9), 783 bytes | 130.00 KiB/s, done.
Total 9 (delta 0), reused 0 (delta 0)
To C:\work\psworkshop\git\remote\TeamRemoteWork.git
 * [new branch]      master -> master

Git Push a simplement fusionner, ou merger, le repository local dans le remote repository. Les commits sont ainsi disponible depuis pour toute personne voulant contribuer au projet

Maintenant plaçons nous dans le répertoire lucien. Lucien veut lui aussi travailler sur le projet DSC et souhaite enrichir le code d’Olivier. Pour cela il doit obtenir une copie du remote repository.

git clone C:\work\psworkshop\git\remote\TeamRemoteWork.git
Cloning into 'TeamRemoteWork'...
done.

Ce que fait git clone, c’est de copier le remote repository en local et d’en faire un repository local et en même temps cela ajout un espace de staging et un espace de travail.

Si l’on regarde à l’interieur du dossier teamremotework on y retrouve notre dossier DSC et un dossier .git

Lucien souhaite corriger deux choses dans ce qui a été fait dans DSC

La première est que l’on souhaite pouvoir utiliser DSC en local mais aussi avec Azure Automation. Dans ce cas il faut que le nom de la configuration corresponde au nom du fichier.

La seconde est de pouvoir aussi effectuer des configurations sur des serveurs linux

Pour la première action

Rename-Item -Path .\dsc\web.dsc.ps1 -NewName iisdeploy.ps1

Pour la seconde on ajoute le fichier ubutunApache.ps1

configuration ubutunApache {
   Import-DSCResource -module "nx"

   Node localhost {

        $requiredPackages = @("apache2","php","php-mysql","mariadb","mariadb-server")
        $enabledServices = @("httpd","mariadb")

        #Ensure packages are installed
        ForEach ($package in $requiredPackages){
            nxPackage $Package{
                Ensure = "Present"
                Name = $Package
                PackageManager = "apt"
            }
        }

        #Ensure daemons are enabled
        ForEach ($service in $enabledServices){
            nxService $service{
                Enabled = $true
                Name = $service
                Controller = "SystemD"
                State = "running"
            }
        }
   }
}

Nous pouvons le rajouter les modifications dans le repos local

git add dsc\ubutunApache.ps1
git add dsc\iisdeploy.ps1

si l’on fait un git status

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   dsc/iisdeploy.ps1
        new file:   dsc/ubutunApache.ps1

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    dsc/web.dsc.ps1

Git par default ne tient pas compte du renomage du fichier web.dsc.ps1 il faut donc l’ajouter

git add dsc\web.dsc.ps1
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    dsc/web.dsc.ps1 -> dsc/iisdeploy.ps1
        new file:   dsc/ubutunApache.ps1

on peut effectuer un commit et le renvoyer vers le repos central

git commit -m "Add Support for Azure and Apache 2 on Ubuntu"
git push origin master

Dès lors ce repos local est synchronisé avec le repository remote. Mais ce n’est pas le cas de notre premier repos celui nommé olivier.

Si l’on revient vers ce dernier, git log n’indique rien

Git Pull, est une des solutions. Git Pull permet de merger les commits du repos remote dans le repos local et d’agir (ajout/suppresion/modification) sur l’espace de travail.

git pull origin master
 * branch            master     -> FETCH_HEAD
Updating 89c5c6d..d9f7ac3
Fast-forward
 dsc/{web.dsc.ps1 => iisdeploy.ps1} |  0
 dsc/ubutunApache.ps1               | 28 ++++++++++++++++++++++++++++
 2 files changed, 28 insertions(+)
 rename dsc/{web.dsc.ps1 => iisdeploy.ps1} (100%)
 create mode 100644 dsc/ubutunApache.ps1

Le git log intégre maintenant l’ensemble des commits

 commit d9f7ac3c9c02a7f3efdbf78f7e7278ea0c966ff4 (HEAD -> master, origin/master)
Author: 
Date:   Sun Jun 16 18:29:01 2019 +0200

    Add Support for Azure and Apache 2 on Ubuntu

commit 89c5c6d9cf024051fc52b6c926800188e4b02ba5
Author:  
Date:   Sun Jun 16 16:04:58 2019 +0200

    Add DSC Folder and web config

commit ef8cf6bdd8b99709d6fe6bff8f80efce2dd68500
Author:  
Date:   Sun Jun 16 15:59:50 2019 +0200

    Add DSC Folder and web config

Durant tout ce temps, Alexandre, clone aussi le remote repository mais n’y fait rien

Maurice ne travaille pas sur le projet DSC. Mais il est chargé de lancer un projet de déploiement à partir d’ARM Template. Autre chose, il travaille sous linux.

Maurice ajoute donc un dossier ARM avec un fichier README.md à l’interieur.
Il commit son travail et exécute un Push

Dès lors comment les autres membres de l’equipe peuvent-ils vérifier que leur repository local est bien à jour.

Cela peut se faire en utilisant un fetch, qui permet de récupérer les changements en remote.

git fetch origin  

git diff  master origin/master 
commit e4f4bd7b7d368d0c3a10266640b9dc2200b7250f (origin/master, origin/HEAD) 

Author: Maurice <olivier@omiossec.work> 

Date:   Sun Jun 16 20:26:18 2019 +0200 

 

    Add ARM Project 

Ici l’on peut voir ce qu’il se passe au niveau de notre remote repository

Mais on se rend compte de plusieurs choses ici.

  • On a rajouté un peu de tout dans le repos central, y compris des choses non terminées

  • Plusieurs personnes ont travaillé sur le même fichier ce qui pourrait créer un conflit s’il avait merger en même temps

C’est ici que l’on doit introduire le concept de branch. Nous avons déjà vu cela lorsque nous avons parlé de master. Une branch est une façon de s’éloigner du travail en cours. C’est une sorte d’univers parallèle où les modifications qui sont faite ne concernent que la branche dans laquelle nous travaillons.

On peut avoir plusieurs stratégies pour ces branches, mais toutes ces stratégies s’accordent sur une chose, la branch master doit rester le plus proche possible de la production, the single source of true, comme pourrait le dire les anglophones.

La branche master peut par exemple service dans une chaine de CI/CD avec Azure DevOps et déclencher des modifications lors d’un commit.

L’une des possibilités de gérer les branches est de créer une branche commune pour chaque nouvelle fonctionnalité, ici une branche ARM et un autre pour DSC et de les merger une fois considérer comme complète.

Dans le même temps si plusieurs développeurs souhaitent travailler sur une même feature, il est d’usage que chacun puisse avoir leur propre branche en local afin d’éviter les complications.

Ainsi à partir du remote repository, nous avons une branche master, correspondant à la prod, plusieurs branches correspondant à nos features et d’autres correspondant au travail des différents acteurs.

Commençons par créer deux branches pour de nouvelles feature, terraform-azure et ps-module-log

sur le repos d’Olivier

git branch terraform-azure 

puis

git branch ps-module log 

En faisant un git branch –a on peut lister les branches en local et en remote

* master 

  ps-module-log 

  terraform-azure 

  Remotes/origin/master 

Ici l’on voit un *, cela correspond à notre HEAD qui est toujours placé sur master

On voit aussi que notre remote n’a pas d’autre branche que master

Pour changer de branche

Git checkout ps-module-log 

Git branche –a nous donne alors

 master 

* ps-module-log 

  terraform-azure 

  Remotes/origin/master 

Créons un nouveau fichier

new-item -Name module-log -Type Directory 

new-item -Path module-log\README.md -ItemType File 

Ajoutons les changements et effectuons un commit

git add module-log\README.md 

git commit -m "New Feature Module Log" 

Tentons un push pour envoyer les modifications

git push origin ps-module-log 
Counting objects: 4, done. 

Delta compression using up to 4 threads. 

Compressing objects: 100% (2/2), done. 

Writing objects: 100% (4/4), 329 bytes | 23.00 KiB/s, done. 

Total 4 (delta 0), reused 0 (delta 0) 

To C:\work\psworkshop\git\remote\TeamRemoteWork.git 

* [new branch]      ps-module-log -> ps-module-log 
git branch –a 
  master 

* ps-module-log 

  terraform-azure 

  remotes/origin/master 

  remotes/origin/ps-module-log 

Maintenant Lucien souhaite aussi pouvoir récupérer les branches pour travailler sur ps-module-log

git fetch origin 
git branch -a 
* master 

  remotes/origin/HEAD -> origin/master 

  remotes/origin/master 

  remotes/origin/ps-module-log 

On voit donc qu’il ne dispose pas en local de la branche ps-module-log . Pour changer cela

Git checkout ps-module-log 
Git branch –a 
 master 

* ps-module-log 

  remotes/origin/HEAD -> origin/master 

  remotes/origin/master 

  remotes/origin/ps-module-log 

Maintenant Lucien veut pouvoir créer une branche locale pour y mettre son développement

Pour cela on doit faire un git branch et un checkout, mais il est possible d’avoir les deux même commandes en une seule

Git checkout –b dev-lucien-ps-module-log 
Switched to a new branch 'dev-lucien-ps-module-log' 
* dev-lucien-ps-module-log 

  master 

  ps-module-log 

  remotes/origin/HEAD -> origin/master 

  remotes/origin/master 

  remotes/origin/ps-module-log 

Nous avons donc une branche locale qui dérive d’une branche remote. On ajoute une ligne dans \module-log\README.md On peut alors faire un commit.

git add .\module-log\README.md 
git commit –m "Dev on Ps Module"

Cependant le but n’est pas d’envoyer cette branche en remote mais de la fusionner avec la branche ps-module-log

Pour cela on se place sur la branch ps-module-log

git checkout ps-module-log 
Switched to branch 'ps-module-log' 

Your branch is up to date with 'origin/ps-module-log'. 

Puis

git merge dev-lucien-ps-module-log 
Updating 55b8015..239dbe2 

Fast-forward 

module-log/README.md | 1 + 

1 file changed, 1 insertion(+) 

Supprimons la branche de dev

git branch -d dev-lucien-ps-module-log 

Il est alors possible d’envoyer les changements vers le remote repository

git push origin ps-module-log 
Counting objects: 4, done. 

Delta compression using up to 4 threads. 

Compressing objects: 100% (2/2), done. 

Writing objects: 100% (4/4), 348 bytes | 69.00 KiB/s, done. 

Total 4 (delta 0), reused 0 (delta 0) 

To C:\work\psworkshop\git\remote\TeamRemoteWork.git 

   55b8015..239dbe2  ps-module-log -> ps-module-log 

C’est ici les bases pour utiliser Git en tant que Ops, il y a bien d’autres options, mais les concepts expliqués ici doivent vous permettre d’avancer dans votre trajet vers le DevOps et l’Infra as Code.

Petite explication sur les prénoms proposés ici, ont une petite explication, ce sont ceux qui existent sur mon état civil (oui que 4).