.. include:: Abbrev.txt Funktionen ********** Häufig ist es sinnvoll, seinen Quellcode in abgeschlossenen Strukturen zu definieren. Die rein sequenzielle Abfolge wird ersetzt durch den Aufruf von Programmcontainern, die eine gewisse Funktionalität bereitstellen. Auf diese Funktionalität kann dann innerhalb des Skriptes immer wieder zugegriffen werden. Folgende Elemente werden beinhaltet: + Funktionskopf (Signatur) + Rückgabewerte + Parameterübergabe Grundsätzlicher Aufbau ====================== .. index:: Funktion, function Funktionen sind selbst definierte Kommandos, die im Prinzip drei Aufgaben haben: - Shorthand: Abkürzungen für einfache Befehle zur Arbeitserleichterung - Combining: Funktionen können Arbeit erleichtern, indem Sie mehrere Arbeitsschritte zusammenfassen können - Kapselung und Erweiterung: .. only:: html .. sidebar:: Tafelbild Arbeitsblatt Funktion .. image:: images/funktion.png :width: 50px :alt: Tafelbild Lehrer :align: left .. image:: images/notizzettel.png :width: 50px :alt: alternate text :align: left :target: _images/notizzettel.png .. image:: images/funktion_schueler.jpg :width: 50px :alt: alternate text :align: left Der grundlegende Aufbau einer Funktion ist immer gleich. Nach dem Funktion-Statement folgt der Name der Funktion und anschließend der Powershell-Codeblock in geschweiften Klammern. Ein Beispiel: .. code-block:: sh Function Cd.. { Cd .. } Cd.. #Für immer wiederkehrende Aufgaben Function myPing { ping.exe -w 100 -n 1 10.10.10.10 } myPing Pinging 10.10.10.10 with 32 bytes of data: Reply from 88.70.64.1: destination host unreachable. #mit einem Argument Function myPing { ping.exe -w 100 -n 1 $args } myPing www.microsoft.com Pinging lb1.www.ms.akadns.net [207.46.193.254] with 32 bytes of data: Request timed out. Ping statistics for 207.46.193.254: Packets: Sent = 1, Received = 0, Lost = 1 (100% Loss), .. only:: latex Tafelbild .. image:: images/notizzettel.png Gerade bei längeren Funktionen kann die Eingabe innerhalb einer Zeile etwas umständlich sein. Eingaben können in |PS| allerdings über mehrere Zeilen gehen. Dies erreicht man durch Drücken von Return, wenn die jeweils aktuelle Zeile noch nicht fertig geschreiben ist. .. image:: images/funktionen_auf_konsole.jpg Spätestens jetzt wird der Einsatz eines Editors bzw. der grafischen Version der |PS| überlegenswert. Eine Darstellung des gesamten Funktion kann man mit folgender Zeile erreichen .. code-block:: sh $function:NextFreeDrive .. image:: images/function_nextfreedrive.jpg Die Ausgabe einer Funktion in eine Textdatei kann man z.B. mit dem Out-File-Commandlet erreichen .. code-block:: sh $function:NextFreeDrive | Out-File -Encoding utf8 nextfreedrive.ps1 notepad .\nextfreedrive.ps1 .. image:: images/function_nextfreedrive_script.jpg .. index:: Argumentübergabe Übergabe von Argumenten ======================= .. only:: html .. sidebar:: Tafelbild Funktionen und Argumente .. image:: images/notizzettel.png :width: 50px :alt: alternate text :align: left :target: _images/notizzettel.png Häufig will man einer Funktion Informationen mit übergeben, die diese dann weiterverarbeiten soll. Dies wird mit Hilfe von sog. **Argumenten** bewerkstelligt. Es gibt 4 verschiedene Arten der Argumentübergabe .. index:: single: Arguments; Arbitrary **Arbitrary arguments:** die $args variable (Array) beinhaltet alle Parameter, die einer Funktion übergeben werden. Damit ist sie eine gute Lösung, wenn eine flexible Anzahl von Parametern übergeben werden soll. .. code-block:: sh function add() { $i = 0 foreach($_ in $args) { $i = $i + $_ } Write-Host $i } .. index:: single: Arguments; Named **Named arguments:** Eine Funktion kann auch pro Parameter einen Namen vergeben. Damit ist die Reihenfolge der Parameter beliebig, weil Sie über den Namen aufgelöst werden. .. code-block:: sh function add1($value1, $value2) { Write-Host ($value1 + $value2) } add1 -value2 10 -value1 10 .. image:: images/function_named_parameters.png .. index:: single: Arguments; Predefined **Predefined arguments:** Parameter können mit default-Werten versehen sein. Falls der Benutzer keine eigenen Werte übergibt, werden die default-Werte genommen. .. code-block:: sh function add2($value1=10, $value2=20) { Write-Host ($value1 + $value2) } add -value2 10 -value1 10 .. image:: images/function_default_value.png .. index:: single: Arguments; Typed **Typed arguments:** Parameter können mit einem bestimmten Datentyp definiert werden, um sicherzustellen, dass nur die Argumente aus "richtigen" Datentypen bestehen. .. code-block:: sh function add([int] $value1, [int] $value2) { $value1 + $value2 } .. image:: images/function_type_misinterpretation.png .. index:: single: Arguments; Special **Special argument types:** Neben herkömmlichen Datentypen können Parameter auch wie ein **switch**-Befehl funktionieren. Wenn der Parametername übergeben wird, dann hat der Parameter den Wert $true, ansonsten $false .. code-block:: sh function add($wert1, $wert2, [switch]$help { if($help) { Write-Host "Hilfe ausgeben" } } add -help .. only:: latex .. image:: images/notizzettel.png :width: 1300px .. index:: Rückgabewert Rückgabewerte ============= In |PS| geben Funktionen nie nur einen Wert zurück sondern immer alles. Wenn man die Funktion lediglich aufruft, gibt die Funktion die Ausgabe über die Konsole aus. Die Ausgabe kann aber auch in einer Variablen gefangen werden. .. code-block:: sh function add3($value1=10, $value2=20) { $value1 + $value2 } $result = add3 -value1 10 -value2 10 $result .. image:: images/function_returnvalue.png **Solange die Funktion nur einen Wert zurückliefert, ist der Rückgabetyp quasi eine Variable. Falls mehrere Ausgaben erfolgen würden, wird die Ausgabe in einen Array gekapselt. Dieser kann von der aufrufenden Seite aus beliebig angepackt werden.** .. code-block:: sh function VAT([double]$amount=0) { $amount * 0.19 } # An interactively invoked function # output results in the console: VAT 130.67 24.8273 # But the result of the function can # also be assign to a variable: $result = VAT 130.67 $result 24.8273 # The result is a single number value # of the "double" type: $result.GetType().Name Double Function VAT([double]$amount=0) { $factor = 0.19 $total = $amount * $factor "Value added tax {0:C}" -f $total "Value added tax rate: {0:P}" -f $factor } The function returns two results: VAT 130.67 Value added tax $24.83 Value added tax rate: 19.00% # All results are stored in a single variable: $result = VAT 130.67 $result Value added tax $24.83 Value added tax rate: 19.00% # Several results are automatically stored in an array: $result.GetType().Name Object[] # You can get each separate result of the # function by using the index number: $result[0] Value added tax $24.83 # The data type of the respective array element # corresponds to the included data: $result[0].GetType().Name String .. image:: images/function_return_array.png .. index:: Ablaufsteuerung Ablaufsteuerung ================ .. only:: html .. sidebar:: Tafelbild Funktionen und Ablaufsteuerung .. image:: images/notizzettel.png :width: 50px :alt: alternate text :align: left :target: _images/notizzettel.png Das Aufteilen des Skriptes in Funktionen teilt den Quellcode in zwei Bereiche auf, nämlich den - der Funktionalität und - der Ablaufsteuerung Da Funktionen zunächst innerhalb des Skriptes überhaupt keine Wirkung besitzen, werden sie erst durch die restlichen Codeteile, der Ablaufsteuerung, zum "Leben erweckt". Die Verbindung zwischen beiden Teilen erfolgt durch die - hereingereichten Parameter und die - zurückgegebenen Ergebnisse. .. only:: latex Tafelbild .. image:: images/notizzettel.png .. index:: Return Return Statement ================ Das RETURN-Statement hat in der |PS| nicht die selbe Bedeutung wie in anderen Programmiersprachen, da Funktionen immer alles zurückgeben. Es wurde dennoch implementiert, vor allem aus 2 Gründen: Style: Man folgte damit den Vorgaben anderer Programmiersprachen, die ebenfalls ein RETURN-Statement haben Beenden einer Funktion: Return beenedet eine Funktion; alle Ausgaben unterhalb des RETURN-Statements würden von der Funktion nicht zurückgegeben werden. .. code-block:: sh Function Add([double]$value1, [double]$value2) { # This time the function returns a whole # series of oddly assorted results: "Here the result follows:" 1 2 3 # Return also returns a further result: return $value1 + $value2 # This statement will no longer be executed # because the function will exit when return is used: "Another text" } Add 1 6 Here the result follows: 1 2 3 7 $result = Add 1 6 $result Here the result follows: 1 2 3 7 Textausgeben verhindern ======================= Häufig hat man bestimmte Ausgaben nur für Kontrollzwecke bzw. Debug-Ausgaben in eine Funktion integriert. Im Produktiveinsatz will man diese Ausgaben nicht sehen und benötigt sie auch nicht. Folgendes Beispiel zeigt mögliche Alternativen: .. code-block:: sh Function Test { "Calculation will be performed" $a = 12 * 10 "Result will be emitted" "Result is: $a" "Done" } Test Calculation will be performed Result will be emitted Result is: 120 Done $result = Test $result Für diesen Zweck gibt es mehrere Möglichkeiten. .. index:: Write-Host Write-Host benutzen: Das Cmdlet Write-Host gibt die Ausgabe sofort an die Konsole weiter .. code-block:: sh Function Test { Write-Host "Calculation will be performed" $a = 12 * 10 Write-Host "Result will be emitted" "Result is: $a" Write-Host "Done" } # This time your debugging reports will already # be output when the function is executed: $result = test Calculation will be performed Result will be emitted Done # The result will no long include your debugging reports: $result Result is: 120 .. index:: Debug-Write, $DebugPreference Debug-Ausgabe benutzen Mit Hilfe des Write-Debug-Cmdlets können Ausgaben nur zu Debug-Zwecken ausgegeben werden. Voraussetzung st allerdings, dass man die globale Variabel $DebugPrefeence auf "SilentlyContinue" stellt .. code-block:: sh Function Test { Write-Debug "Calculation will be performed" $a = 12 * 10 Write-Debug "Result will be emitted" "Result is: $a" Write-Debug "Done" } # Debugging reports will remain completely # invisible in the production environment: $result = Test # If you would like to debug your function, # turn on reporting: $DebugPreference = "Continue" # Your debugging reports will now be output # with the "DEBUG:" prefix and output in yellow: $result = Test DEBUG: Calculation will be performed DEBUG: Result will be emitted DEBUG: Done # They are not contained in the result: $result Result is: 120 # Everything is running the way you wish; # turn off debugging: $DebugPreference = "SilentlyContinue" $result = Test .. index:: $errorActionPreference Fehlermeldungen unterdrücken: Fehlermeldungen werden normalerweise immer sofort ausgegeben, was natürlich den Ablauf eines Skriptes stören kann. Auch hier gibt es wieder eine globale Variable, welches die Ausgabe von Fehlermeldungen steuern kann. .. code-block:: sh Function Test { # Suppress all error messages from now on: $ErrorActionPreference = "SilentlyContinue" Stop-Process -name "Unavailableprocess" # Immediately begin outputting all error messages again: $ErrorActionPreference = "Continue" 1/$null } # Error messages will be suppressed in certain # areas but not in others: $result = Test .. index:: Inline-Help Inline-Help von Funktionen ============================ Um den Nutzer einer Funktion auch innerhalb der Powershell mit Informationen über die Funktion versorgen zu können, kann man sowohl Funktionen als auch das ganze Skript mit einer sog. **Inline-Help** ausstatten. Im Grunde handelt es sich dabei um eine Abfolge von Kommentarzeilen, die mit bestimmten Schlüsselworten versehen worden sind. Details der Anwendung gibt es unter http://technet.microsoft.com/en-us/library/dd819489.aspx .. code-block:: sh <# .SYNOPSIS Dieses Skript gibt eine Liste der nicht funktionierenden Hardware aus mit Hilfe von WMI. .DESCRIPTION Per WMI werden zunächst die Systemdetails ermittelt und dann die Hardware mit Fehlern. .NOTES File Name : Get-BrokenHardware.ps1 Author : Karl Steinam - teetscher Requires : PowerShell Version 2.0 .LINK Siehe auch: http://www.meineFirma.de .EXAMPLE PSH [C:\foo]: Get-BrokenHardware.ps1 Computer Details: Manufacturer: Dell Inc. Model: Precision WorkStation T7400 Service Tag: 6Y84C3J Hardware thats not working list Description: WD My Book Device USB Device Device ID: USBSTOR\OTHER&VEN_WD&P\70332&1 Error ID: 28 #> # Display Computer details "Computer Details:" $comp = gwmi Win32_ComputerSystem "Manufacturer: {0}" -f $comp.Manufacturer "Model: {0}" -f $comp.Model $computer2 = Get-WmiObject Win32_ComputerSystemProduct "Service Tag: {0}" -f $computer2.IdentifyingNumber "" #Get hardware that is errored "Hardware that's not working list" $broken = Get-WmiObject Win32_PnPEntity | where ` {$_.ConfigManagerErrorCode -ne 0} ` #Display broken hardware foreach ($obj in $broken){ "Description: {0}" -f $obj.Description "Device ID: {0}" -f $obj.DeviceID "Error ID: {0}" -f $obj.ConfigManagerErrorCode "" } .. image:: images/inline-help_1.jpg .. image:: images/inline-help_2.jpg Das nächste Beispiel fügt einer Funktion eine Inline-Help hinzu. .. code-block:: sh <# .SYNOPSIS Adds a file name extension to a supplied name. .DESCRIPTION Adds a file name extension to a supplied name. Takes any strings for the file name or extension. .PARAMETER Name Specifies the file name. .PARAMETER Extension Specifies the extension. "Txt" is the default. .INPUTS None. You cannot pipe objects to Add-Extension. .OUTPUTS System.String. Add-Extension returns a string with the extension or file name. .EXAMPLE C:\PS> extension -name "File" File.txt .EXAMPLE C:\PS> extension "File" "doc" File.doc .LINK http://www.fabrikam.com/extension.html .LINK Set-Item #> function Add-Extension2 { param ([string]$Name,[string]$Extension = "txt") $name = $name + "." + $extension $name } Je nach Laune kann die Inline-Help auch innerhalb der Funktion geschrieben werden. Größeres Beispiel bzgl. Funktionen ================================== Siehe: http://www.powershellpro.com/powershell-tutorial-introduction/powershell-scripting-with-wmi/ http://www.powershellpro.com/computernames-activedirectory/149/ http://www.computerperformance.co.uk/powershell/powershell_example_basic.htm#Example_4:_PowerShell_Real-life_Task http://www.powershellpro.com/powershell-tutorial-introduction/powershell-scripting-with-wmi/ http://technobeans.wordpress.com/2010/11/25/powershell-getting-inventory-details/ **Vorausetzung:** Installiertes CIM-Studio zur Abfrage des WMI Im folgenden Beispiel sollen mit Hilfe von WMI verschiedene Informationen des lokalen Rechners abgerufen werden. In weiteren Schritten wird die Suche auf beliebige Rechner ausgeweitet. Im letzten Schritt werden Informationen des ActiveDirectory genutzt, um eine komfortable Liste der Rechner zu erhalten. **Scenario:** Ihr Chef will eine Inventur der Hardware aller Server und Rechner im Netzwerk. Insbesondere will er folgende Informationen. * Machine manufacturer, model number, and serial number. * BIOS information to determine if updates are required, * OS type * CPU information: Manufacturer, type, speed, and version. * Amount of memory in each server. * Disk information: Size, interface type, and media type. * Network Information: IP settings and MAC address. .. index:: WMI Benutzung von WMI ================= WMI ist im Grunde eine Datenbank von Informationen, das auf jeden Windows System vorhanden ist. Über den sog. WMI-Service kann man die jeweils gewünschten Daten abrufen. .. image:: images/cimcpu.JPG Von Powershell aus ist die Vorgehensweise relativ einfach: :-) .. code-block:: sh Get-WmiObject -List -Namespace "root\CIMV2" .. image:: images/wmi_classes.JPG Eine etwas kleinere Ausgabe gibt es, wenn man die Ausgabe auf bestimmte Inhalte beschränkt, in unserem Beispiel .. code-block:: sh Get-WmiObject Win32_ComputerSystem | Format-List * .. image:: images/wmi_win32_suche.png Welche Informationen entsprechen den Anforderungen ? * Manufacturer = Manufacturer property. * Model Number = Model property. * Serial Number = Nicht vorhanden; sie muss anderweitig gesucht werden * Gesamtspeicher = TotalPhysicalMemory-Eigenschaft existiert in der Klasse und genügt unseren Ansprüchen. **Schritt 1: Machine manufacturer, model number, and serial number** .. code-block:: sh :linenos: #sets the computer to local $strComputer ="." #Creates a variable called $colItems which contains the WMI Object $colItems = Get-WmiObject Win32_ComputerSystem -Namespace "root\CIMV2" -ComputerName $strComputer #Use a foreach loop to iterate the $colItems (collection). #Store the properties information in the $objItem Variable. foreach($objItem in $colItems) { #Use the Write-Host cmdlet to output required property information Write-Host "Computer Manufacturer: " $objItem.Manufacturer Write-Host "Computer Model: " $objItem.Model Write-Host "Total Memory: " $objItem.TotalPhysicalMemory "bytes" } **Schritt 2: BIOS-Information** Die entsprechende Klasse im WMI ist Win32_BIOS .. code-block:: sh Get-WmiObject Win32_BIOS | Format-List * .. code-block:: sh :linenos: $strComputer = "." $colItems = Get-WmiObject Win32_BIOS -Namespace "root\CIMV2" -computername $strComputer foreach($objItem in $colItems) { Write-Host "BIOS:"$objItem.Description Write-Host "Version:"$objItem.SMBIOSBIOSVersion"." $objItem.SMBIOSMajorVersion"."$objItem.SMBIOSMinorVersion Write-Host "Serial Number:" $objItem.SerialNumber } **Schritt 3: Übrige Informationen** *OS TYPE:* .. code-block:: sh :linenos: $strComputer = "." $colItems = Get-WmiObject Win32_OperatingSystem -Namespace "root\CIMV2" -Computername $strComputer foreach($objItem in $colItems) { Write-Host "Operating System:" $objItem.Name } *CPU Info:* .. code-block:: sh :linenos: $strComputer = "." $colItems = Get-WmiObject Win32_Processor -Namespace "root\CIMV2" -Computername $strComputer foreach($objItem in $colItems) { Write-Host "Processor:" $objItem.DeviceID $objItem.Name } *DISK Info:* .. code-block:: sh :linenos: $strComputer = "." $colItems = Get-WmiObject Win32_DiskDrive -Namespace "root\CIMV2" -ComputerName $strComputer foreach($objItem in $colItems) { Write-Host "Disk:" $objItem.DeviceID Write-Host "Size:" $objItem.Size "bytes" Write-Host "Drive Type:" $objItem.InterfaceType Write-Host "Media Type: " $objItem.MediaType } *NETWORK Info:* .. code-block:: sh :linenos: $strComputer = "." $colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace "root\CIMV2" -ComputerName $strComputer | where{$_.IPEnabled -eq "True"} foreach($objItem in $colItems) { Write-Host "DHCP Enabled:" $objItem.DHCPEnabled Write-Host "Subnet Mask:" $objItem.IPSubnet Write-Host "Gateway:" $objItem.DefaultIPGateway Write-Host "MAC Address:" $ojbItem.MACAddress } .. index:: Datei, Alias, notation, Random **Kapseln der Code-Schnipsel in Funktionen** Zur übersichtlicheren Handhabung werden wir die einzelnen Codeelemente in Funktionen kapseln. Alle Funktionen werden in die Datei ServerInventory.ps1 geschrieben. .. code-block:: sh :linenos: Function SysInfo { $colItems = Get-WmiObject Win32_ComputerSystem ` -Namespace "root\CIMV2" -ComputerName $strComputer ` foreach($objItem in $colItems) { Write-Host "Computer Manufacturer: " $objItem.Manufacturer Write-Host "Computer Model: " $objItem.Model Write-Host "Total Memory: " $objItem.TotalPhysicalMemory "bytes" } } Function BIOSInfo { $colItems = Get-WmiObject Win32_BIOS -Namespace "root\CIMV2" ` -computername $strComputer ` foreach($objItem in $colItems) { Write-Host "BIOS:"$objItem.Description Write-Host "Version:"$objItem.SMBIOSBIOSVersion"." $objItem.SMBIOSMajorVersion"."$objItem.SMBIOSMinorVersion Write-Host "Serial Number:" $objItem.SerialNumber } } Function OSInfo { $colItems = Get-WmiObject Win32_OperatingSystem -Namespace ` "root\CIMV2" -Computername $strComputer ` foreach($objItem in $colItems) { Write-Host "Operating System:" $objItem.Name } } Function CPUInfo { $colItems = Get-WmiObject Win32_Processor -Namespace ` "root\CIMV2" -Computername $strComputer ` foreach($objItem in $colItems) { Write-Host "Processor:" $objItem.DeviceID $objItem.Name } } Function DiskInfo { $colItems = Get-WmiObject Win32_DiskDrive -Namespace ` "root\CIMV2" -ComputerName $strComputer ` foreach($objItem in $colItems) { Write-Host "Disk:" $objItem.DeviceID Write-Host "Size:" $objItem.Size "bytes" Write-Host "Drive Type:" $objItem.InterfaceType Write-Host "Media Type: " $objItem.MediaType } } Function NetworkInfo { $colItems = Get-WmiObject Win32_NetworkAdapterConfiguration ` -Namespace "root\CIMV2" -ComputerName $strComputer | ` where{$_.IPEnabled -eq "True"} foreach($objItem in $colItems) { Write-Host "DHCP Enabled:" $objItem.DHCPEnabled Write-Host "IP Address:" $objItem.IPAddress Write-Host "Subnet Mask:" $objItem.IPSubnet Write-Host "Gateway:" $objItem.DefaultIPGateway Write-Host "MAC Address:" $ojbItem.MACAddress } } In Powershell müssen die Funktionen vor der ersten Benutzung definiert worden sein. Der Aufruf des Funktionen erfolgt dann einfach **am Ende** des Skriptes. .. code-block:: sh :linenos: #*============================================================= #* SCRIPT BODY #*============================================================= #* Connect to computer $strComputer = "." #* Call SysInfo Function Write-Host "Sytem Information" SysInfo Write-Host #* Call BIOSinfo Function Write-Host "System BIOS Information" BIOSInfo Write-Host #* Call OSInfo Function Write-Host "Operating System Information" OSInfo Write-Host #* Call CPUInfo Function Write-Host "Processor Information" CPUInfo Write-Host #* Call DiskInfo Function Write-Host "Disk Information" DiskInfo Write-Host #* Call NetworkInfo Function Write-Host "Network Information" NetworkInfo Write-Host #*============================================================= #* END OF SCRIPT: [ServerInventory] #*============================================================= **Abfrage eines anderen Computers** Zur Zeit fragt das Skript nur die Daten des eigenen Computers ab. Um auch remote arbeiten zu können, muss die Variable *$strComputer* ersetzt werden. .. code-block:: sh $strComputer = Read-Host "Enter Computer Name" Write-Host "Computer:" $strComputer **Optimierungen** Erzeugen Sie eine Textdatei und fügen Sie Computernamen ein. Erweiteren Sie das Skript so, dass es die Textdatei ausliest und den jeweiligen Computer abfragt. .. image:: images/computers.JPG .. code-block:: sh :linenos: #*=============================================================== #* SCRIPT BODY #*=============================================================== #* Create and array from C:\MyScripts\Computers.txt $arrComputers = get-Content -Path "C:\MyScripts\Computers.txt" foreach ($strComputer in $arrComputers) { #Function Calls go here Write-Host "Computer Name:" $strComputer Write-Host "======================================" #* Call SysInfo Function Write-Host "Sytem Information" SysInfo Write-Host #* Call BIOSinfo Function Write-Host "System BIOS Information" BIOSInfo Write-Host #* Call OSInfo Function Write-Host "Operating System Information" OSInfo Write-Host #* Call CPUInfo Function Write-Host "Processor Information" CPUInfo Write-Host #* Call DiskInfo Function Write-Host "Disk Information" DiskInfo Write-Host #* Call NetworkInfo Function Write-Host "Network Information" NetworkInfo Write-Host "End of Report for $strComputer" Write-Host "======================================" Write-Host Write-Host } #End function calls. **Auslesen der Computer aus dem AD** Um sich die Eingabe der Computer in eine Textdatei zu ersparen, kann man auch innerhalb einer Windows-Domäne das ActiveDirectory befragen. Folgendes Skript liefert die Daten der Computernamen innerhalb des AD. .. code-block:: sh :linenos: #GetPCNames.ps1 $strCategory = "computer" $objDomain = New-Object System.DirectoryServices.DirectoryEntry $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearcher.SearchRoot = $objDomain $objSearcher.Filter = ("(objectCategory=$strCategory)") $colProplist = "name" foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) } $colResults = $objSearcher.FindAll() foreach ($objResult in $colResults) { $objComputer = $objResult.Properties; $objComputer.name } Speichern Sie das Skript als GetPCNames.ps1, starten Sie es innerhalb ihrer Domäne und speichern Sie das Ergebnis in der Datei Computers.txt .. code-block:: sh .\GetPCNames.ps1 > "C:\MyScripts\Computers.txt" .. index:: Parameter, Validierung Erweiterte Parameter ===================== **Validierung** Häufig ist es so, dass die Parameter einer Funktion einer Überprüfung bedürften, bevor diese innerhalb einer Funktion benutzt werden. Dies könnte z.B. sein - Ist überhaupt ein Parameter übergeben worden - Hat der Parameter die richtigen Werte - In der Regel führt dies dazu, dass der Quellcode innerhalb der Funktion durch Fehlerbehandlungscode erweitert wird, bevor man überhaupt die eigentliche Aufgabe löst. **Bsp:** Im unteren Beispiel soll eine Funktion nur bestimmte Werte für die Parameter akzeptieren sollen. - Name: Tom, Dick, Jane - Alter: Zwischen 21 und 65 - Übergebener Dateipfad soll vorhanden sein http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/15/simplify-your-powershell-script-with-parameter-validation.aspx .. code-block:: sh Function Foo { Param( [String] $Name, [Int] $Age, [string] $Path ) Process { If ("Tom","Dick","Jane" -NotContains $Name) { Throw "$($Name) is not a valid name! Please use Tom, Dick, Jane" } If ($age -lt 21 -OR $age -gt 65) { Throw "$($age) is not a between 21-65" } IF (-NOT (Test-Path $Path -PathType 'Container')) { Throw "$($Path) is not a valid folder" } # All parameters are valid so New-stuff" write-host "New-Foo" } } Das (noch nicht bekannte) **throw**-Statement gibt eine Fehlermeldung aus und beendet die Funktion. Diese Vorgehensweise hat mehrere Nachteile: - Ein Großteil des Skriptes ist der Fehlerbehandlung gewidmet. - Die Validierung ist nur so gut wie der sie validierende Quellcode - Die Fehlermeldungen müssen bei Änderungen immer wieder angepasst werden. Aus all diesen Gründen wurden in der Powershell die Möglichkeit der **Parameter-Validierung** eingebaut. Zu den Parametern werden in eckigen Klammern weitere Informationen hinzugefügt, die von der PS zur Laufzeit ausgewertet werden können. Besser gescripted, sieht der Quellcode wie folgt aus: .. code-block:: sh Function Foo { Param( [ValidateSet("Tom","Dick","Jane")] [String] $Name, [ValidateRange(21,65)] [Int] $Age, [ValidateScript({Test-Path $_ -PathType 'Container'})] [string] $Path ) Process { write-host "New-Foo" } } Der Vorteil dieser Methode ist, dass die Fehlerbehandlung nun implizit von der PS vorgenommen wird, inkl. der Bereitstellung und Ausgabe der Fehlermeldungen. Insgesamt stehen über 11 Validierungsoptionen zur Verfügung; diese können unter http://technet.microsoft.com/en-us/library/dd347600.aspx bzw über **get-help about_functions** abgefragt werden. .. index:: Mandatory **Mandatory** Das Mandatory-Argument gibt an, dass ein Parameter erwartet wird. Falls kein Parameter übergeben wird, bricht die Funktion ab. .. code-block:: sh Function Foo { Param ( [parameter(Mandatory=$true)] [String[]] $ComputerName ) } .. index:: Help-Message Häufig wird das Manadatory-Argument durch das Help-Message-Argument ergänzt, welches eine Fehlerbeschreibung zum nicht übergebenen Mandatory-Parameter hinzufügt. Bsp: .. code-block:: sh Function Foo { Param ( [parameter(mandatory=$true, HelpMessage="Enter one or more computer names separated by commas.")] [String[]] $ComputerName ) } Funktionen in Profilen ======================= http://www.howtogeek.com/126469/how-to-create-a-powershell-profile/ Häufig hat es ein Admin gerne, wenn beim Start der Powershell-Konsole bereits bestimmte Grundfunktionalitäten vorhanden sind. PS kennt hierfür das Konzept der Profildateien, die automatisch beim Start der PS mit geladen werden. Zunächst sollte geprüft werden, ob es bereits eine Profildatei gibt. Dies kann man durch Ausgabe der Umgebungsvariable $Profile erreichen. .. code-block:: sh Test-Path $Profile Falls noch keine Datei existiert kann diese mit jedem beliebigem Editor erzeugt werden,ansonsten auch mit .. code-block:: sh New-Item –Path $Profile –Type File –Force In diese Datei kann jeder Befehl, CommandLet, Modul-Import eingestzt werden. Folgendes Beispiel verdeutlicht die Möglichkeiten. .. code-block:: sh Set-ExecutionPolicy unrestricted $ui = (Get-Host).UI.RawUI $ui.ForegroundColor = "Black" $ui.BackgroundColor = "Gray" $ui.WindowTitle = $env:userdomain + "\" + $env:username + " 's Powershell" function prompt { Write-Host "PS " -nonewline -foregroundcolor Red Write-Host $(get-location) "#" -nonewline return " " } $snapins = Get-PSSnapin -Registered $snapins | Add-PSSnapin Get-Module -ListAvailable | Import-Module Get-PSSnapin | Format-Table -autosize PSVersion, Name Get-Module | Format-Table -autosize ModuleType, Name function ff ([string] $glob) { get-childitem -recurse -include $glob } function osr { shutdown -r -t 5 } function osh { shutdown -h -t 5 } function rmd ([string] $glob) { remove-item -recurse -force $glob } function whoami { (get-content env:\userdomain) + "\" + ` (get-content env:\username); } ` function strip-extension ([string] $filename) { [system.io.path]::getfilenamewithoutextension($filename) } function New-PSSecureRemoteSession { param ($sshServerName, $Cred) $Session = New-PSSession $sshServerName -UseSSL ` -Credential $Cred -ConfigurationName C2Remote ` Enter-PSSession -Session $Session } function New-PSRemoteSession { param ($shServerName, $Cred) $shSession = New-PSSession $shServerName ` -Credential $Cred -ConfigurationName C2Remote ` Enter-PSSession -Session $shSession } function prompt { $promptText = "PS>"; $wi = [System.Security.Principal.WindowsIdentity]::GetCurrent() $wp = new-object 'System.Security.Principal.WindowsPrincipal' $wi if ( $wp.IsInRole("Administrators") -eq 1 ) { $color = "Red" $title = "**ADMIN** on " + (hostname); } else { $color = "Green" $title = hostname; } write-host $promptText -NoNewLine -ForegroundColor $color $host.UI.RawUI.WindowTitle = $title; return " " } function gotoprofile{set-location C:\Users\paul.cowan\Documents\WindowsPowerShell} function gotodownloads{set-location C:\Users\paul.cowan\Downloads} function openscratch{notepad C:\users\paul.cowan\desktop\scratch.txt} function opencatscratch{cat C:\users\paul.cowan\desktop\scratch.txt} set-alias notepad "C:\Program Files (x86)\Notepad++\notepad++.exe" set-alias zip "C:\Program Files\7-Zip\7z.exe" set-alias grep select-string set-alias ssh New-PSSecureRemoteSession set-alias sh New-PSRemoteSession set-alias grep select-string set-alias ssh New-PSSecureRemoteSession set-alias sh New-PSRemoteSession set-alias tr gototrunk set-alias pr gotoprojects set-alias profile gotoprofile set-alias build gotobuild set-alias downloads gotodownloads set-alias nc gotoncontinuity2 set-alias lead gotolead set-alias live gotocurrent set-alias live_build gotocurrentbuild set-alias scratch openscratch set-alias catscratch opencatscratch set-alias ie "C:\Program Files\Internet Explorer\iexplore.exe" set-alias c2 gotoc2 set-alias c2web "C:\projects\continuity2\ncontinuity2.web.sln" Urls ======== http://technet.microsoft.com/en-us/library/dd347600.aspx http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/22/use-powershell-to-make-mandatory-parameters.aspx http://www.simple-talk.com/dotnet/.net-tools/down-the-rabbit-hole--a-study-in-powershell-pipelines,-functions,-and-parameters/ http://cleancode.sourceforge.net/wwwdoc/articles.html http://powershell.isdecisions.com/ http://blogs.technet.com/b/heyscriptingguy/archive/2012/05/21/understanding-the-six-powershell-profiles.aspx