Question [Fonction]Gestion de prérequis

Plus d'informations
il y a 11 ans 10 mois #11784 par Laurent Dardenne
Salut,
voici une fonction permettant de valider une suite de prérequis ordonnés.
Ici un prérequis est constitué d'un nom et d'une suite d'instructions. Une hashtable d'un type particulier les héberge et permet d'ordonner vos prérequis.
Rien ne sert, par exemple, de valider l'existence d'une ressource sur un serveur distant si celui-ci n'est pas joignable...

La clé d'une entrée de cette hashtable porte le nom du prérequis, en cas d'échec celui-ci sera utilisé dans le message d'erreur.
La valeur associé à cette clé est un scriptblock, utilisé en local ou à distance via WinRM.
On déclare deux hashtable, une pour les préquis locaux, la seconde pour les distants.

Régle de validation des prérequis, libre à vous de modifier le comportement :
On exécute tous les prérequis sur une machine.
On passe à l'étape suivante du traitement si tous la totalité des prérequis réussissent,
on quitte sur une exception dés que l'un d'eux échoue.

La fonction de validation des prérequis :
[code:1]
function Test-Prerequisit {
#Valide les prérequis à l'exécution d'un traitement
#On exécute les tests les uns à la suite des autres, on quitte sur une exception dés qu'un prérequis échoue.
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateNotNull()]
$Computer,
[Parameter(Position=1, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
#Utilise une interface et pas une classe
[System.Collections.IDictionary] $Rules,
[Parameter(Position=2, Mandatory=$false)]
[System.Management.Automation.PSCredential] $Credential,
[switch] $Distant
)
if ($Distant)
{$Type='distant'}
else
{$Type='local'}

#Enumére tous les prérequis à valider
$Rules.GetEnumerator()|
Foreach {
$CurrentRule=$_
Write-Debug \"Valide le prérequis :$($CurrentRule.Name)\"
try
{
#Enumére tous les prérequis à valider
& $CurrentRule.Value > $null
}
catch
{
throw \"[Erreur sur le pré-requis $type $($CurrentRule.Key)]: $($_)\"
}
}
} #Test-Prerequisit
[/code:1]
Les déclarations des prérequis :
[code:1]
#Hashtable ordonnée
$Rules=new-object System.Collections.Specialized.OrderedDictionary

#La valeur de chaque clé est un Scriptblock
#Celui-ci contient un test d'une régle de gestion, il renvoi donc soit $true soit $false ou une exception
#$Computer est un paramètre de la fonction Test-Prerequisit
#Pour indiquer une erreur de validation de la règle on déclenche une exception.
#Une erreur est bloquante.

$Rules.PowerShellVersion={
#Validation du numéro de version du runtime PowerShell

$Result=Test-Path Variable:«»PSVersionTable

if ($Result)
{ $Result=$PSVersionTable.PSVersion -ge ([Version]\"2.0\"«») }

#$Computer est une variable locale définie dans la portée de l'appelant
if ($Result -eq $false)
{Throw \"La version de Powershell sur le serveur $Computer doit être supérieure ou égale à 2.\"}
} #VersionPowerShell

# $Rules.EchecVolontaire={
#
# Throw \"Prerequis en erreur.\"
#
# } #TestErreur

$Rules.PowerShellExecutionPolicy={
#Validation de la police d'exécution de PowerShell

$ExecutionPolicy=Get-ExecutionPolicy
if ($ExecutionPolicy -match \"AllSigned|Restricted|Default|Undefined\"«»)
{Throw \"La police d'exécution de Powershell($ExecutionPolicy) sur le serveur $Computer doit être RemoteSigned.\"}

} #ExecutionPolicy

$RulesDistant=new-object System.Collections.Specialized.OrderedDictionary

#$WinRMCredential est un paramètre de la fonction Test-Prerequisit
$RulesDistant.PowerShellVersion={
#Validation du numéro de version du runtime PowerShell

#Une adaptation peut être nécessaire selon votre configuration
#$ResultFromDistant=Invoke-Command -computername $Computer -scriptblock {
$ResultFromDistant=Invoke-Command -computername $Computer -Credential $WinRMCredential -scriptblock {

$ErrorActionPreference=\"Stop\"
$Result=Test-Path Variable:«»PSVersionTable

if ($Result)
{ $Result=$PSVersionTable.PSVersion -ge ([Version]\"2.0\"«») }
#$env:ComputerName référencee le serveur distant sur lequel ce code est exécuté
if ($Result -eq $false)
{Throw \"La version de Powershell sur le serveur $env:ComputerName doit être supérieure ou égale à 2.\"}
} #invoke
} #VersionPowerShell
[/code:1]
La construction des credentials reste à votre charge, car chaque organisation/outil propose la sienne.
Un exemple :
[code:1]
function New-AccountInfo($account,$pwd)
{
New-Object PSObject -Property @{Account=$Account;Pwd=$pwd}
}

function New-Credential ($AccountInfo)
{
#Pwd ne doit être ni nul ni vide
$password=convertto-securestring $AccountInfo.pwd -AsPlainText -force
#Username (account) ne doit être ni nul ni vide
new-object -typename System.Management.Automation.PSCredential -argumentlist $AccountInfo.account,$password
}
$WinRMCredential= New-Credential (New-AccountInfo 'Laurent' 'password')
[/code:1]
Enfin l'appel de la fonction de validation des prérequis :
[code:1]

$ServerLocal=$Env:ComputerName
Test-Prerequisit $ServerLocal $Rules

$ServerDistant=$Env:ComputerName
#Une adaptation peut être nécessaire selon votre configuration WinRM
Test-Prerequisit $ServerDistant $RulesDistant $WinRMCredential -Distant
[/code:1]
Pour les appels distant je suppose que WinRm est correctement configuré, même si vous utilisez une seule machine.

Un autre point, et non des moindres, peut être considéré :
l'analyse des erreurs distantes (possible, mais laborieux), voir l'analyse des erreurs distantes générés lors de l'appel d'un programme externe( utilisez la redirection : prg.exe 2 > &1).

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 11 ans 10 mois #11790 par Matthew BETTON
:ohmy:

Mon cerveau a réussi à dépiler !

Ca c'est dynamique !

Très intéressant.

Merci Laurent ;)

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 11 ans 10 mois #11791 par Matthew BETTON

Une adaptation peut être nécessaire selon votre configuration WinRM


Comme par exemple si seul le listener https est activé (-UseSSL) ou le port a été modifié (-Port) au niveau du Invoke-Command.

Pour les appels distant je suppose que WinRm est correctement configuré, même si vous utilisez une seule machine.


Si besoin, effectuer au préalable un Test-WSMan ...

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 11 ans 10 mois #11792 par Laurent Dardenne
Matthew BETTON écrit:

Comme par exemple si seul le listener https est activé (-UseSSL) ou le port a été modifié (-Port) au niveau du Invoke-Command.

Tout à fait.
Matthew BETTON écrit:

Si besoin, effectuer au préalable un Test-WSMan ...

C'est exactement ce que je fais !
Matthew BETTON écrit:

Ca c'est dynamique !

Alors une autre version ++:
[code:1]
Function New-PreRequisite{
param (
[Parameter(Position=0, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $Name,
[Parameter(Position=1, Mandatory=$true)]
[ValidateNotNull()]
[ScriptBlock] $Code,
[switch] $StopOnError
)

if ($code.ToString().Trim() -eq [string]::Empty)
{Write-Error \"The scriptblock `$Code is empty.\"}
else
{
$ValidatePreRequisite=[scriptblock]::Create(@\"
try {
$Code
} catch {
`$false
}
\"@
)
$O=New-Object PSObject -property @{Name=$Name;Success=$null;StopOnError=$StopOnError;Datas=$null}
Add-Member -InputObject $O -MemberType ScriptMethod -Name Validate -value $ValidatePreRequisite
$O.PsObject.TypeNames[0] = \"PreRequisiteRule\"
$O
}
}#New-PreRequisite

$req= @(
New-PreRequisite -StopOnError \"Windows Seven or greater\" {
$this.Datas=Resolve-OSVersion
@(\"Win7\",\"W2K8\",\"W2K8R2\"«») -Contains $this.Datas
}
;
New-PreRequisite -StopOnError \"Dotnet framework 4.0\" {
Test-Path \"HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client\"
}
)

$Req[0]
# Name Datas StopOnError Success
# ----


# Windows Seven or greater True

$Req[0].validate.script
[/code:1]
Ici je veux tester une condition (Validate), mémoriser le résultat(Success), et récupérer, pour un usage ultérieure, la valeur renvoyée par le code(Data).
A l'origine j'avais ce besoin pour créer un setup.
L'objectif à terme étant de fusionner les deux approches pour avoir qq chose dans ce genre là :
[code:1]
$First= New-PreRequisite -StopOnError \"Dotnet framework 4.0\" $Msg.Fx4 {
$this.Datas=New-DictionaryEntry 'isExistFramework40' (Test-Path \"HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client\"«»)
$this.Datas.Value
}|
Add-DependentePreRequisite -StopOnError \"Dotnet framework 3.0\" $Msg.Fx3 {
$this.Datas=New-DictionaryEntry 'isExistFramework30' (Test-Path \"HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3\Client\"«»)
$this.Datas.Value
} -passtrhu
#Les dépendances étant basées sur la classe Collections.Generic.LinkedList[PSObject]
[/code:1]
En local et/ou en distant :silly:

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.075 secondes
Propulsé par Kunena