PowerShell Outsourcing der Settings für automatisierte PowerShell-Scripts - Methoden in Hashtable'n

DPXone

Lieutenant
Registriert
Mai 2009
Beiträge
554
Hallo Community,

aktuell bin ich gerade mit der Ausarbeitung für eine sinnvolle, einfach handhabare und global einsetzbare Umsetzung für Konfigurations-Dateien für PowerShell-Scripts mit Script-Methoden beschäftigt.

Insbesondere beim Einsatz von GIT ist es natürlich unerlässlich sämtliche Konfigurations-Dateien, welche Endpoint-URIs, Anmeldedaten, Ablageordner, etc enthalten, auszugliedern.
Alles was dynamisch seien soll muss ausgeliedert werden. --> Änderungen am Code (selbst wenn es nur ein Dateipfad ist) wären ein Change im Code, obwohl es nur externe Parameter sind.

Ein vorgeschaltetes Script oder ultimative Parameter sind nicht in meinem Sinne!

Ziel:
Ich will ein PS-Script (auch gerne ein Tool in C#) erstellen, welches via "Export-Clixml" (in C# natürlich anders) eine für das jeweilige Script nutzbare Konfigurations-Datei erstellt, die diverse Properties dynamisch zur Laufzeit aktualisiert und bereitstellt (-->"ScriptMethod" im Falle von PS).

Mein Anfang:
Konfig-Daten auszugliedern ist absolut kein Problem. Entweder über herkömmliches XML,
Oder eben über CliXml, welches auch allerhand Type-Daten mitgibt und ganz wichtig ... PS-Credentials (zumindest das Passwort) out-of-the-box direkt in einen Encrypted Standard String umwandelt (kann man natürlich auch manuell ins Script mit "ConvertFrom-SecureString" einbauen).

Das Konfig-Script würde bei Credentials für Service Accounts mit "Run As ..." ausgeführt werden, damit der Encrypted Standard String (bzw. Secure String nach dem Einlesen) auch nur für den Service Account nutzbar ist. ("Im-/Export-CliXml"-cmdlet verarbeitet Dinge wie SecureString ohne eigenes Zutun.)


Herausforderung:
Ich versuche nun herauszufinden, wie ich direkt in der Hashtable (--> wohl eher Object), die für die Erstellung der Konfig-Datei zuständig ist, Methoden vom Typ "ScriptMethod" hinzufüge, welche unter anderem dann bei Abruf das aktuelle Datum+Uhrzeit und/oder das Root-Verzeichnis zurückgibt, in welchem das finale Script ausgeführt wird.

Wichtig dabei, die Member des Objekts sollten auf Member desselben Objekts Zugriff haben.
Grund ist, damit man global z. B. ein Root-Verzeichnis für Log-Files, ausgehend vom Script-Pfad, definieren kann.

Wie es ja bisher aussieht, speichert die die Cli-konforme XML nur Daten über Properties und nicht über Methoden ....

Missing:
  • Script Methoden via "Export-CliXml" exportieren
  • Properties dynamisch gestalten
  • Im Object für diverse notwendige Infos auf andere Member des selben "Main-Objekts" zuzugreifen, welche auch schon aktualisiert wurden (durch die Script-Ausführung) (Siehe Beispiel)-

(an PS angelehnter) Pseudocode für die Generierung von Konfig-Files:
Beachtet insbesondere die Pseudo-Generierung von "$settings.Global.Log.ErrorFilePath"
Code:
$settings = @{
    Global  = @{
        Date = Get-Date
        DateFormat = 'yyyy-MM-dd' # international date format
        DateTimeFormat =  'yyyy-MM-dd HH-mm-ss.fffK' # international dateTime format with milliseconds and time offset
        Log = @{
            RootFolderPath = $PSScriptRoot  # <- dynamical; set on script execution
            ErrorFilePath = join-path -Path '<<$settings/Global/Log/RootFolderPath" (not relying on $PSScriptRoot)>>' -ChildPath '<<$settings.Global.Date.toString($settings.Global.DateFormat)_error.csv>>'
            WarningFilePath = ''
            InfoFilePath = ''
        }
    }

    SystemX = @{
        Global   = @{
            BaseURI = ''

            Log = @{
                RootFolderPath = ''  # <- dynamical; set on script exectioin
            }
        }

        API = @{
            Global = @{
                Credentials =  [pscredential]::new('APIUser','23473288fd7883478943893.......')  # <- password is from type secure-string
            }
            OData = @{
                BaseEndpint = ''
            }

            SOAP = @{
                BaseEndpoint = ''
            }
        }
    }
    SFTP    = @{
        Global   = @{
            Crededentials = [pscredential]::new()
            Method = ''


        }
    }
}

$settings | Export-Clixml  -Depth ([int]::MaxValue) -LiteralPath C:\temp\settings.xml -Encoding utf8


Aktueller Stand:
Irgendwie finde ich leider nichts anwendbares darüber ... aktuell.
Werde auf jeden Fall mal weiter forschen.

Falls ihr direkt eine Idee oder gar einen KnockOut-Kommentar habt, dann bitte gerne her damit.

Viele Grüße
Sven
 
Zuletzt bearbeitet:
Nur zur Klarstellung: Du hast schon parametrisierte Scripts, aber bist sozusagen nicht in der Lage (wegen Vorgabe) jene Parameter dynamisch festzulegen, sprich Du brauchst eine Konfigurationsdatei, wo die Parameter drinstehen, von wo sie dann vom Script ausgelesen und mit welchen dieses dann seine Parameter belegen kann. In etwa richtig?

Zunächst würde ich die Finger von CliXML lassen. Das ist absolut nicht portabel, da hast Du mehr Probleme mit als daß es Dir nützt.
Sinnvoller wäre da imo JSON als Übergabeformat, insbesondere, da das so flache Strukturen wie hashtables fast perfekt ist (json: schlüssel: wert; hashtable: schlüssel = wert). Dann muß man nur noch per Import-Json die Daten wieder aus der JSON-Datei einlesen (und hat seine Hashtable gleich wieder) und kann die dann via Splatting ans Script übergeben -- du brauchst also nur ein "Bootstrap-Script", welches eben jene Parameter beschafft und dann das eigentliche Script startet, mit dem Ergebnis, daß keine Änderungen am Code erforderlich sein sollten.

Denk dran daß der exportierte Securestring nicht entschlüsselt werden muß, um verwendet werden zu können, sprich aufpassen daß diese gut geschützt sind.


Methoden gehen allerdings nicht zu serialisieren. Da mußt Du Dir was anderes einfallen lassen.
Sehe aber auch keinen Grund dafür. Nicht Methoden sind zu übergeben, sondern Parameter für diese Methoden; im Zweifel mußt Du noch eine (statische) Objektklasse bauen und in die Library aufnehmen, welche dann die Daten aus der serialisierten Übergabe verarbeiten kann.

Also statt Hashtable mit Eintrag function Bootstrap-Me() stattdessen einen Eintrag BootstrapParameters = HashTable() in der Hashtable (sodaß sich ganz einfach fllexibel viele benannte Parameter übergeben lassen) und zusätzlich eine allgemeine Methode Process-BootstrapData -Input HashTable, wo hinten das rauskommt, was Du benötigst. Das kann sogar ein Scriptblock sein, welcher sich dann mit Invoke-Command aufrufen läßt und Du hättest etwas ähnliches zu Deinem ursprünglichen Ansatz.
 
  • Gefällt mir
Reaktionen: DPXone
Nach längerer Zeit jetzt noch eine Antwort von mir nach meinem Like ;)
Wieder mal besten Dank für die ausführliche Antwort :)

Ja, JSON war eigentlich auch mein erster Ansatz.
Dachte nur, dass man über CliXML eventuell irgendwie weitergehen könnte was Methoden oder so betrifft (zumindest im Windows-Umfeld).

RalphS schrieb:
Denk dran daß der exportierte Securestring nicht entschlüsselt werden muß, um verwendet werden zu können, sprich aufpassen daß diese gut geschützt sind.
Den Satz versteh ich nicht ganz.
Der SecureString kann nur von dem User genutzt werden, der ihn erstellt hat (mehrmals getestet u.a. unbewusst auf meinem Rechner mit meinem User und nicht dem ServiceUser)
Wenn man einen SecureString, bzw. den "Encrypted Standard String" (einzige Art es wegzuspeichern), einliest, der nicht vom eigenen User erstellt wurde, bekommt man direkt eine Exception ausgeworfen (ist ja auch der Sinn dahinter).
Oder hast du hier schon eine Hintertür entdeckt? :O

Werde mal weiter schauen, wie ich meinen Ansatz dann am besten umsetze, um das Ganze modular über Konfig-Files zu realisieren.


Nachtrag bezüglich der User-Abhängigkeit was den SecureString angeht.
Hier mal ein Beispiel was bei mir beim Handhaben von SecureString rauskommt.
Kannst ja mal versuchen den "Encrypted Standard String" unten im Result bei dir zu parsen in einen SecureString (Code siehe Zeile 7)
PowerShell:
$secureString = ConvertTo-SecureString 'Abc123Hallo' -AsPlainText -Force
$encryptedStandardString = ConvertFrom-SecureString -SecureString $secureString

Write-Host 'Encrypted Standard String' -ForegroundColor Yellow
$encryptedStandardString

$secureString = ConvertTo-SecureString -String $encryptedStandardString
$plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString))

Write-Host "`r`nPlain Password" -ForegroundColor Yellow
$plainPassword

Result:
Code:
Encrypted Standard String
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000cc98e6dd0f41a9468e6629599b084162000000000200000000001066000000010000200000009b0ab1f3707ff1a765cf11a2521716cd9449e7f9678c962a3b850a0e9ac1c711000000000e800000000200002000000008aa441b0418c29fa24fecc29833cafbc7d56c4bd506ce58c591bdc1fc1a126b20000000ab9803659b036a7120651108f478d4bc1420151a1b74dd0de38021359d30eb6c4000000054e6bcaf7a8b4e085fd56d2d279790fb65de7f2e83cb0bfe6dfba508ada0cbe77e3172c152ab8d27696dfde49a34e26f5a7552dae0196da28bd5eb77116abbea

Plain Password
Abc123Hallo
 
Zuletzt bearbeitet:
Zurück
Oben