Ha már vannak ilyen futásidőben kiszámított részeink a konfigurációsfájlban jön az ötlet, hogy hozzunk létre feltételes ágakat. Ez lehetővé tenné, hogy más adatok olvasódjanak be a fejlesztői környezetben és mások az „éles” rendszerben. Az alábbi PSD1 fájlban van két „Conditional” ág: a Conditional_3_Prod akkor érvényesül, ha a benne definiált feltétel (Condition) „igazra” értékelődik ki, ami jelen esetben akkor történik meg, ha a szkriptet a „ProdServer” nevű gépen futtatjuk. Ekkor a korábbi AccessToRestApi adatblokk felülbírálódik az új értékekkel, illetve a ScriptConfig-nak a textvalue adata is, valamint születne egy új adatblokk is, a UniqueToProd:
@{
# This section is used by this
specific script
ScriptConfig
= @{
textvalue = "SomeText"
date = {[datetime] "2022.12.01"}
bool = $true
}
# This should be shared between
other scripts
AccessToRESTApi = @{
AppID = 'd24099e5-10cb-4388-8ef4-f1c9f374d50b'
Token = 'UG93ZXJTaGVsbElzR3JlYXQh'
ApiURL = 'https://api.myapp.com/rest'
}
# This is a default setting for
all scripts
LogSettings = @{
LogFolder = {join-path $PSScriptMyRoot Logs}
subhive = @{
subdynamicdata = {[math]::Sqrt(9)}
}
}
Conditional_3_Prod = @{
Condition = {$env:COMPUTERNAME -eq 'ProdServer'}
AccessToRESTApi = @{
AppID = 'd24099e5-10cb-4388-1111-f1c9f374d50b'
Token = 'prodprodprod'
ApiURL = 'https://apiprod.myapp.com/rest'
}
ScriptConfig = @{
textvalue = "ProdText"
}
UniqueToProd = @{
ProdValue = "This is the unique
value"
}
}
Conditional_2_PreProd = @{
condition = {$true}
ScriptConfig = @{
textvalue = "PreProdText"
}
}
}
Felvettem még egy feltételes adatblokkot is abból a célból, hogy ezek kiértékelésének sorrendjét tudjam tesztelni. A sorrendiségüket a conditional_sorszám_név elnevezésükben a sorszám határozza meg.
Nézzük a továbbfejlesztett ParseConfig függvényt a példaszkriptben:
# This is a production script
#region Functions
function ParseConfig {
param(
$path = "$($PSScriptFullPath).psd1"
)
function ResolveDynamicData {
param([hashtable] $confighive)
foreach($key in $confighive.Clone().Keys){
if($confighive.$key -is [hashtable]){
ResolveDynamicData -confighive $confighive.$key
}
elseif($confighive.$key -is [scriptblock]){
$confighive.$key = &(& $confighive.$key)
}
}
}
function MergeHives {
param(
[hashtable] $hive,
[hashtable] $target
)
$hiveclone = $hive.Clone()
foreach($h in $hiveclone.Getenumerator()){
if($h.key -match '^Condition'){
continue
}
elseif($h.value -isnot [hashtable] -or !$target.ContainsKey($h.key)){
$target.($h.key) = $h.value
}
else{
MergeHives -hive $h.value -target $target.($h.key)
}
}
}
$PSConfig = Import-PowerShellDataFile -Path $path
ResolveDynamicData -confighive $PSConfig
$PSConfigClone = $PSConfig.Clone()
foreach($key in ($PSConfigClone.keys -match '^Conditional_' | Sort-Object)){
if($PSConfigClone.$key.condition){
MergeHives -hive $PSConfig.$key -target $PSConfig
}
$PSConfig.Remove($key)
}
$PSConfig
}
#endregion
#########################################################
#
# Body
#
#########################################################
$PSScriptFullPath = $myinvocation.mycommand.path
$PSScriptMyRoot = $PSScriptRoot
$PSConfig = ParseConfig
Lett egy új függvény: MergeHives. Ez fésüli össze majd azokat az adatblokkokat, amelyeket egy feltételes blokk érvényre jutásakor be kell illeszteni a konfigurációs hashtáblába. Ez a függvény két hashtábla paramétert vár, az egyik az az adatblokk, amit be kell fésülni ($hive), a másik, ahova az adatokat be kell tölteni ($target). Itt is klónozok, a $hiveclone ágain végig megyek. Ha az ág maga a feltétel (condition), az átlépem, hiszen arra már nincs szükségem mert ide már csak akkor jutok, ha a feltétel igazra értékelődött ki. Ha az ág nem hashtábla vagy a $target-ben még nincs meg az adott ág, akkor egyszerűen beteszem vagy felülírom a $target-be. Ha viszont az ág maga is egy hashtábla, akkor rekurzívan meghívom újra a MergeHives függvényt, immáron ennek az ágnak az adattartalmával és az egyel mélyebb célággal.
A ParseConfig további része most úgy néz ki, hogy szintén a PSD1 fájl importálásával kezdek, majd a szkriptblokkokat feloldom, majd klónozok és a klónban kikeresem az immár kiértékelt feltételes blokkokat a blokk nevének sorrendjében és amennyiben a feltétel igaz, akkor az összefűzéssel beleintegrálom az eredeti $PSConfig-ba.
Ellenőrizzük a működését, elsőként úgy, hogy egyik feltételes ág sem igaz:
PS C:\> . C:\PSTools\Section05-Video3Script.ps1
PS C:\> $PSConfig
Name Value
---- -----
AccessToRESTApi {AppID, Token, ApiURL}
LogSettings {subhive, LogFolder}
ScriptConfig {date, textvalue, bool}
PS C:\> $PSConfig.ScriptConfig.textvalue
SomeText
Ebben a változatban nincsen UniqueToProd adatblokk és a textvalue is az eredeti SomeText értéket tartalmazza.
Most beélesítem a Conditional_3_Prod ágat azzal, hogy a feltételébe egy $true-t teszek, nézzük, mi lesz most a $PSConfig:
PS C:\> . C:\PSTools\Section05-Video3Script.ps1
PS C:\> $PSConfig
Name Value
---- -----
ScriptConfig {date, textvalue, bool}
LogSettings {subhive, LogFolder}
AccessToRESTApi {AppID, Token, ApiURL}
UniqueToProd {ProdValue}
PS C:\> $PSConfig.ScriptConfig.textvalue
ProdText
Látható, hogy most már megjelent a UniqueProd ág és a textvalue is már ProdText lett. Ha most beélesítem a Conditional_2_PreProd részt is, akkor így alakul a végső konfiguráció:
PS C:\> $PSConfig
Name Value
---- -----
ScriptConfig {date, textvalue, bool}
condition True
UniqueToProd {ProdValue}
AccessToRESTApi {AppID, Token, ApiURL}
LogSettings {subhive, LogFolder}
PS C:\> $PSConfig.ScriptConfig.textvalue
ProdText
Egyelőre nem látunk változást, a textvalue még mindig ProdText, hiszen a Prod résznek van a legnagyobb sorszáma, így az ottani adatok értékelődnek ki legkésőbb, azaz azok felülírják a PreProd hatását. Viszont ha átírom a Prod rész sorszámát 1-re: Conditional_1_Prod, akkor már más lesz a helyzet:
PS C:\> . C:\PSTools\Section05-Video3Script.ps1
PS C:\> $PSConfig
Name Value
---- -----
ScriptConfig {date, textvalue, bool}
UniqueToProd {ProdValue}
AccessToRESTApi {AppID, Token, ApiURL}
LogSettings {subhive, LogFolder}
PS C:\> $PSConfig.ScriptConfig.textvalue
PreProdText