nagus
Goto Top

Powershell CustomObject richtig verwenden und Verhalten von Variablen bei geschweiften Klammern

Hi zusammen,

vorne weg - ich bin noch am lernen von PS ....

Im Rahmen der Useranlage möchte ich mir ein CustomObject erstellen , in das ich relevante Werte schreibe.

Feste Werte sind die ID und die Email Adresse, welche aus einer CSV eingelesen werden. Mit einer Schleife arbeite ich das ganze ab und erzeuge die User. Nun ich möchte nun pro User ein zufälliges Passwort, dass ich später an bestimmte Personen als Email aus dem Script verschicke.
Das hätte ich nun gerne so wie ich die Daten aus den CSVs lade, nämlich tabellarisch, damit ich die gezielt ansprechen kann.

$mail = 'max.mustermann@world.de'  

function Get-RandomCharacters($length, $characters) {
    $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs=""  
    return [String]$characters[$random]
}
 
function Scramble-String([string]$inputString){     
    $characterArray = $inputString.ToCharArray()   
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length     
    $outputString = -join $scrambledStringArray
    return $outputString 
}
 
$password = Get-RandomCharacters -length 7 -characters 'abcdefghiklmnoprstuvwxyz'  
$password += Get-RandomCharacters -length 2 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'  
$password += Get-RandomCharacters -length 1 -characters '1234567890'  
$password += Get-RandomCharacters -length 1 -characters '!§$%&()=?@#*+-'  
 
$password = Scramble-String $password



function Get-RandomCharacters($length, $characters) {
    $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs=""  
    return [String]$characters[$random]
}
 
function Scramble-String([string]$inputString){     
    $characterArray = $inputString.ToCharArray()   
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length     
    $outputString = -join $scrambledStringArray
    return $outputString 
}
 
$userid = Get-RandomCharacters -length 4 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'  
$userid += Get-RandomCharacters -length 2 -characters '1234567890'   

$userid = Scramble-String $userid

$PasswortListeTemp = [PSCustomObject]@{
     PSTypeName = 'PasswortListe.Temp'  
     UserID           = $UserId
     EmailEmpfaenger = 'bla.bla@world.de'  
     Passwort         = $password
     }

$PasswortListeTemp

Soweit so gut. Aber wie schreibe ich jetzt pro Runde in der ein User angelegt wird die neuen Daten in eine neue Zeile? Ich habe mir schon einen Wolfgesucht aber nix gefunden ...

Außerdem verstehe ich nicht, warum ich nur noch Statische Werte erhalte, wenn ich den Block oben in {}-Klammern setze:

$mail = 'max.mustermann@world.de'  
{
function Get-RandomCharacters($length, $characters) {
    $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs=""  
    return [String]$characters[$random]
}
 
function Scramble-String([string]$inputString){     
    $characterArray = $inputString.ToCharArray()   
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length     
    $outputString = -join $scrambledStringArray
    return $outputString 
}
 
$password = Get-RandomCharacters -length 7 -characters 'abcdefghiklmnoprstuvwxyz'  
$password += Get-RandomCharacters -length 2 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'  
$password += Get-RandomCharacters -length 1 -characters '1234567890'  
$password += Get-RandomCharacters -length 1 -characters '!§$%&()=?@#*+-'  
 
$password = Scramble-String $password



function Get-RandomCharacters($length, $characters) {
    $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs=""  
    return [String]$characters[$random]
}
 
function Scramble-String([string]$inputString){     
    $characterArray = $inputString.ToCharArray()   
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length     
    $outputString = -join $scrambledStringArray
    return $outputString 
}
 
$userid = Get-RandomCharacters -length 4 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'  
$userid += Get-RandomCharacters -length 2 -characters '1234567890'   

 
$userid = Scramble-String $userid

   


$PasswortListeTemp = [PSCustomObject]@{
     PSTypeName = 'PasswortListe.Temp'  
     UserID           = $UserId
     EmailEmpfaenger = 'bla.bla@world.de'  
     Passwort         = $password
     }
}
$PasswortListeTemp


Ich dachte die Variablen wären global ....

Thx
Nagus

Content-ID: 614368

Url: https://administrator.de/contentid/614368

Ausgedruckt am: 21.11.2024 um 22:11 Uhr

GrueneSosseMitSpeck
GrueneSosseMitSpeck 20.10.2020 aktualisiert um 23:36:01 Uhr
Goto Top
waren sie, bis Powershell 4.0

Vor ein paar Jahren kam dann die Revolution, Variablen hatten plötzlich einen Scope face-sad und Subroutinen / Funktionen erfordern zwingend eine Übergabe von allen Parametern.

Ich meine mit Windows 2016 CTP kam PS 5.0 und diese Version konnte das dann endlich... bzw. ich war auch sehr angepisst, denn daß Microsoft in einer Skritpsprache, die es von überallher zusammengeklaut hat, dann so grundsätzliche Dinge ändert, das fand ich schon mal gelinde gesagt gemein.

Damit mein Skript - das diverse Features in OS zwishcen Windows 7 und 2019 aktiviert und dann im IIS ein paar Sites erzeugt - portabel blieb mußte ich die als Parameter übergebenen Variablen umbenennen damit es auf PS4 und PS5 läuft.
TK1987
Lösung TK1987 21.10.2020 aktualisiert um 09:59:22 Uhr
Goto Top
Moin,

warum definierst du die Funktionen Get-RandomCharacters & Scramble-String im Script doppelt? Das ist unnötig, einmal definiert kannst du eine Funktion so oft aufrufen wie du willst.
Zitat von @Nagus:
function Get-RandomCharacters($length, $characters) {
    $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
    $private:ofs=""  
    return [String]$characters[$random]
}
 
function Scramble-String([string]$inputString){     
    $characterArray = $inputString.ToCharArray()   
    $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length     
    $outputString = -join $scrambledStringArray
    return $outputString 
}
 
$password = Get-RandomCharacters -length 7 -characters 'abcdefghiklmnoprstuvwxyz'  
$password += Get-RandomCharacters -length 2 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'  
$password += Get-RandomCharacters -length 1 -characters '1234567890'  
$password += Get-RandomCharacters -length 1 -characters '!§$%&()=?@#*+-'  
 
$password = Scramble-String $password

Das funktioniert so zwar, aber hier ein kleiner Tipp, der direkt beide Funktionen vereint:
$lower='abcdefghiklmnoprstuvwxyz'.ToCharArray()  
$upper='ABCDEFGHKLMNOPRSTUVWXYZ'.ToCharArray()  
$digit='1234567890'.ToCharArray()  
$punct='!§$%&()=?@#*+-'.ToCharArray()  

function Get-RandomPassword {
  $Password = @(
    1..7 | %{ $lower | Get-Random }   # 7 zufällige kleine Buchstaben
    1..2 | %{ $upper | Get-Random }   # 2 zufällige Großbuchstaben
    $digit | get-random               # 1 zufällige Zahl
    $punct | get-random               # 1 zufälliges Sonderzeichen
  )
  
  $Password = ( $Password | Get-Random -Count $Password.count ) -Join ''  
  return $Password
}
Hinweis: das % ist ein Alias für Foreach-Object


Soweit so gut. Aber wie schreibe ich jetzt pro Runde in der ein User angelegt wird die neuen Daten in eine neue Zeile?
Wenn du es mit Foreach--Object machst, musst du halt für jede Zeile das PSCustomObject mit den entsprechenden Werten neu erstellen.
Alternativ kannst du jedoch auch mit Select-Object einfach Spalten an einem Object ergänzen, was in deinem Fall mit bestehender CSV-Datei wohl die bessere Wahl wäre.
Die ergänzten Spalten müssen nur folgendem Muster entsprechen:
@{ Name = "Spaltenname" ; Expression = { <# Code zur Ermittlung des Wertes #> }}  
Hinweis: Name & Expression kann man auch einfach mit n & e abkürzen.

Gehe ich Recht in der Annahme, dass PSTypeName immer gleich sein soll? Das sähe dann am Ende so aus:
$CSV = 'C:\Pfad\Datei.csv'  
Import-Csv -path $CSV -Delimiter ';' | Select-Object @{n='PSTypeName';e={'PasswortListe.Temp'}},UserID,EmailEmpfaenger,@{n='Passwort';e={Get-RandomPassword}}  
Wenn du in deiner Csv-Datei ein anderes Trennzeichen als ein Semikolon verwendest, musst du -Delimiter natürlich anpassen. Darüber hinaus muss du, sofern du keine Spaltenüberschreiften in deiner CSV-Datei hast, noch -Header UserID,EmailEmpfaenger ergänzen.

Außerdem verstehe ich nicht, warum ich nur noch Statische Werte erhalte, wenn ich den Block oben in {}-Klammern setze
Was in Geschweiften Klammern steht wird zum Codeblock und muss dann mit & davor aufgerufen werden, sofern es ausgeführt werden soll. Macht in deinem Fall jedoch wenig Sinn und eine weiterführende Erklärung wäre hier wohl erst mal zu viel Input.

Ich dachte die Variablen wären global ....
Wie ist diese Aussage zu verstehen? Variablen sind per default zum Glück nicht global - so ist es möglich, in Funktionen teils die selben Variablennamen zu nutzen ohne sich Gegenseitig zu stören.
Jedoch können die Variablen auch auf unterschiedliche Weise gesetz werden:
$Variable          = 'xxx' # Lokale Variable, gilt nur innerhalb eines Moduls  
$Local:Variable    = 'xxx' # Selbes wie oben, nur mit Bereichsangabe  
$Global:Variable   = 'xxx' # Globale Variable, gilt Skriptübergreifend in der kompletten Instanz  
$Script:Variable   = 'xxx' # Gilt im gesamten Skript  
$Private:Variable  = 'xxx' # Private Variable gilt nur innerhalb eines Bereichs  
siehe hierzu auch
help about_scopes

Gruß Thomas
Nagus
Nagus 21.10.2020 aktualisiert um 19:30:28 Uhr
Goto Top
Hi Thomas,
vielen Dank für Deine ausführliche Hilfe!

Eigentlich hatte ich gestern bereits die Lösung gefunden, aber manchmal sieht man ja den Wald vor lauter Bäumen nicht.

Mit dem Export als CSV habe ich schon meine Erfahrungen gemacht ... das war nicht das Problem face-wink
Den Rest muss ich noch ein wenig verdauen.

Dank Deiner Hilfe habe ich nun auch noch grob verstanden, wie ich mir Funktionen baue face-big-smile Kann ich die eigentlich an beliebiger stelle im Script ablegen und benutzen? Also beispielweise alle Funktionen am Anfang erstellen und dann später nur noch aufrufen?

$PWL = @()

$lower='abcdefghiklmnoprstuvwxyz'.ToCharArray()  
$upper='ABCDEFGHKLMNOPRSTUVWXYZ'.ToCharArray()  
$digit='1234567890'.ToCharArray()  
$punct='!§$%&()=?@#*+-'.ToCharArray()  

function Get-RandomPassword {
  $Password = @(
    1..7 | %{ $lower | Get-Random }   # 7 zufällige kleine Buchstaben
    1..2 | %{ $upper | Get-Random }   # 2 zufällige Großbuchstaben
    $digit | get-random               # 1 zufällige Zahl
    $punct | get-random               # 1 zufälliges Sonderzeichen
  )
  
  $Password = ( $Password | Get-Random -Count $Password.count ) -Join ''  
  return $Password
}

for ($i=1; $i -le 10; $i++) {

                $UserID = "Test$i"  
                $Passwort = Get-RandomPassword
                
                $PWL += [PSCustomObject]@{
                    PSTypeName = 'PWL.Temp'  
                    UserID = $UserID
                    Passwort = $Passwort
                    }

}
Insofern ist Dein Code etwas hübscher face-wink

Aber Vielleicht kannst Du mir ja noch einen Hinweis zum CustomObject geben: wofür ist der PSTypeName? Ich habe was dazu gefunden aber nicht kapiert ...

Vielen Dank
Nagus
TK1987
TK1987 21.10.2020 um 20:05:57 Uhr
Goto Top
Zitat von @Nagus:
Kann ich die eigentlich an beliebiger stelle im Script ablegen und benutzen? Also beispielweise alle Funktionen am Anfang erstellen und dann später nur noch aufrufen?
Ja, sie müssen halt nur zu dem Zeitpunkt, an dem du sie aufrufen möchtest, bereits definiert sein - alle Funktionen am Anfang erstellen geht also wunderbar, alle Funktionen ans Ende des Skripts stellen geht jedoch nicht.

Aber Vielleicht kannst Du mir ja noch einen Hinweis zum CustomObject geben: wofür ist der PSTypeName?
Deiner Frage nach zu urteilen: Für nichts 🤣.

Da du diese Spalte bei dir im Code so definiert hattest, habe ich sie übernommen. Ich habe ja extra oben gefragt, ob die in jeder Zeile den gleichen Inhalt haben soll.

Wenn du diese Spalte also nicht brauchst, lass sie einfach weg.