Több konfigurációs fájl használata

Ha megnézzük ezt a PSD1 fájl tartalmát láthatjuk, hogy a 3 benne található szekció más célt szolgál. A ScriptConfig rész olyan adatokat tartalmaz, ami a szkriptünk saját információit tartalmazza. Az AccessToRESTApi-ban egy webszolgáltatás eléréséhez szükséges adatok vannak, ezt a részt több szkript is használhatja, így ezeket az adatokat jó lenne egy külön fájlba rakni, hogy elég legyen egy helyen karbantartani az adatokat. A LogSettings részben olyan adatok vannak, ami akár az összes szkriptünkre érvényes lehet, így azokat egy harmadik fájlban kellene tárolni. Lássuk ezeket a fájlokat! Az első a fő szkriptünkhöz tartozó PSD1 fájl:

# Section05-Video4Script.ps1.psd1

@{

    # This section is used by this specific script

    Action = {$script:PSEnvironment = 'DEV'}

    ScriptConfig = @{

        textvalue = "SomeText"

        date = {[datetime] "2022.12.01"}

        bool = $true

    }

 

    Conditional_1_Prod = @{

        Condition = {$env:COMPUTERNAME -eq 'ProdServer'}

        Action = {$script:PSEnvironment = 'PROD'}

        ScriptConfig = @{

            textvalue = "ProdText"

        }

 

        UniqueToProd = @{

            ProdValue = "This is the unique value"

        }

    }

 

    Conditional_2_PreProd = @{

        condition = {$true}

        ScriptConfig = @{

            textvalue = "PreProdText"

        }

    }

}

Látható, hogy itt csak tényleg a szkript saját beállításai vannak már, se a RESTApi dolgok, se a globális beállítások nincsenek itt. Van viszont egy új adattípus, az „Action”. Ezek csak azért vannak, hogy a szkriptblokkok feloldásakor ezek végrehajtódjanak, de ténylegesen nem kell majd eltárolni a konfigurációs adatok közé. Ezekben az Action bejegyzésekben elsősorban az aktuális környezet típusát határozom meg és tárolom el egy szkriptszintű változóba, hogy a többi, közös konfigurációs fájl feltételes részei számára tudjuk szolgáltatni a bemeneti adatokat, mint például „DEV” vagy „PROD”. Így majd például a RESTApi konfigurációs fájlban ezalapján lesz kiválasztva, hogy éppen mely feltételes részek aktiválódjanak. Mivel a többi konfigurációs fájlt más szkriptek is használják, így oda nem tehetünk „beégetett” feltételeket, mert ami az egyik szkript számára PROD, az a másik számára DEV vagy akár ismeretlen.

Nézzük tehát, hogy ez alapján hogyan néz ki a RestAPIConfig.psd1:

# RestAPIConfig.psd1

@{

    # This should be shared between other scripts

    AccessToRESTApi = @{

        AppID = 'd24099e5-10cb-4388-8ef4-f1c9f374d50b'

        Token = 'UG93ZXJTaGVsbElzR3JlYXQh'

        ApiURL = 'https://api.myapp.com/rest'

    }

 

    Conditional_1_Prod = @{

        Condition = {$script:PSEnvironment -eq 'PROD'}

        AccessToRESTApi = @{

            AppID = 'd24099e5-10cb-4388-1111-f1c9f374d50b'

            Token = 'prodprodprod'

            ApiURL = 'https://apiprod.myapp.com/rest'

        }

    }

}

Látszik, hogy ebben már a feltételes részben az előbb látott $script:PSEnvironment változó szerepel. A globalconfig.psd1 egyszerűbb az én esetemben, itt nincs feltételes rész:

# globalconfig.psd1

@{

    # This is the default setting for all scripts

    LogSettings = @{

        LogFolder = {join-path $PSScriptMyRoot Logs}

        subhive = @{

            subdynamicdata = {[math]::Sqrt(9)}

        }

    }

}

Egészítsük tehát ki a ParseConfig függvényünket, hogy több konfigurációs fájlt is képes legyen beolvasni, illetve az új Action bejegyzésekkel is elbánjon:

# This is a production script

 

#region Functions

function ParseConfig {

param(

    [string[]]$paths

)

    function ResolveDynamicData {

    param([hashtable] $confighive)

       

        foreach($key in ($confighive.Clone().Keys | Sort-Object -Property {

                    if($_ -match '^Condition$'){"zz$($_)"}

                    elseif($_ -match 'Action'){"zzz$($_)"}

                    elseif($_ -match '^Conditional_'){"zzzz$($_)"}

                    else{"__$($_)"}

                }

            )

        ){

            if($confighive.$key -is [hashtable]){

                ResolveDynamicData -confighive $confighive.$key

            }

            elseif($confighive.$key -is [scriptblock] -and (!$confighive.ContainsKey('Condition') -or $confighive.Condition)){

                $confighive.$key = &(& $confighive.$key)

            }

        }

    }

 

    function MergeHives {

        param(

            [hashtable] $hive,

            [hashtable] $target = $PSConfig

        )

 

        foreach($h in $hive.Clone().Getenumerator()){

            if($h.key -match '^Condition|^action$'){

                continue

            }

            elseif($h.value -isnot [hashtable]){

                $target.($h.key) = $h.value

            }

            elseif(!$target.ContainsKey($h.key)){

                if($h.value.containskey('action')){

                    $h.value.remove('action')

                }

                $target.($h.key) = $h.value

            }

            else{

                MergeHives -hive $h.value -target $target.($h.key)

            }

        }

    }

 

    if($paths -notcontains "$($PSScriptFullPath).psd1" -and (Test-Path "$($PSScriptFullPath).psd1")){

        $paths = @("$($PSScriptFullPath).psd1") + $paths

    }

 

    $PSConfig = @{}

   

    foreach($path in $paths){

        if(!(Test-Path -Path $path)){

            Write-Error "No config file was found at '$path'"

            continue

        }

 

        $config = Import-PowerShellDataFile -Path $path

 

        ResolveDynamicData -confighive $Config

 

        $ConfigClone = $Config.Clone()

 

        foreach($key in ($ConfigClone.keys -notmatch '^Condition' | Sort-Object)){

            MergeHives -hive $Config

        }

 

        foreach($key in ($ConfigClone.keys -match '^Conditional_' | Sort-Object)){

            if($ConfigClone.$key.condition){

                MergeHives -hive $Config.$key

            }

        }

    }

 

    $PSConfig

}

#endregion

 

#########################################################

#

# Body

#

#########################################################

 

$PSScriptFullPath = $myinvocation.mycommand.path

$PSScriptMyRoot   = $PSScriptRoot

$PSConfig = ParseConfig -paths "$PSScriptMyRoot\globalconfig.psd1", "$PSScriptMyRoot\RestAPIConfig.psd1"

Nézzük először a függvény törzsét! A $path paraméterben immár több elérési út is lehet, így azokat majd egy foreach ciklussal fogom feldolgozni. Előtte megnézem, hogy a szkript nevének megfelelő PSD1 fájl a $path-ban szerepel-e és ha nem, viszont megtalálható a szkript mappájában, a szkript mellett, akkor azt még hozzárakja a $path értékeihez a legelső helyre, hogy mindenképpen az legyen kiértékelve elsőként. Ezután jön a foreach, mellyel minden konfigurációs fájlt importálok, majd feloldom bennük a szkriptblokkokat majd beintegrálom a MergeHives-al a nem Condition kezdetű kulcsokat a $PSConfig változóba, majd azokat a Conditional_ kulcsokat, amelyeknek a Condition ága igazra értékelődött ki.

A ResolveDynamicData-ban annyi változtatás lett, hogy most fontos most a kulcsok sorrendje, ezt a Sort-Object-nek átadott dinamikus tulajdonság-definícióval oldottam meg. Ez úgy működik, hogy a „Condition” nevű kulcsok neve elé „zz”-t tesz, az „Action” kulcsok elé „zzz”, a „Conditional_*” nevűek elé „zzzz”-t, az összes többi kulcs neve elé „__”-t. Ezzel azt érem el, hogy először az „egyéb” kulcsokat oldja fel, majd a feltételeket, majd az akciókat és végül a feltételes blokkok belsejét, így csak azokat az akciókat hajtja végre, ahol már a feltétel igazra értékelődött ki.

Ha futtatom a szkriptet az eredeti feltételek szerint (a gépem neve nem ProdServer), akkor ezt kapom:

PS C:\> . C:\PSTools\Section05-Video4Script.ps1

PS C:\> $PSEnvironment

DEV

PS C:\> $PSConfig.AccessToRESTApi

 

Name                           Value

----                           -----

AppID                          d24099e5-10cb-4388-8ef4-f1c9f374d50b

Token                          UG93ZXJTaGVsbElzR3JlYXQh

ApiURL                         https://api.myapp.com/rest

Ha viszont $true-t teszek a Conditional_1_Prod feltételébe, akkor ezt:

PS C:\> . C:\PSTools\Section05-Video4Script.ps1

PS C:\> $PSEnvironment

PROD

PS C:\> $PSConfig.AccessToRESTApi

 

Name                           Value

----                           -----

AppID                          d24099e5-10cb-4388-1111-f1c9f374d50b

Token                          prodprodprod

ApiURL                         https://apiprod.myapp.com/rest

Ezzel elértük, hogy a konfigurációs fájlok a feltételek alapján automatikusan állítják össze az összes beállítási adatot, se a szkripthez, de még csak a konfigurációs fájlokhoz sem kell nyúlnunk, így az változtatásokból eredő hibalehetőségek valószínűségét gyakorlatilag nullára csökkentettük.



Word To HTML Converter