Kategorieauswahl

PowerShell

PowerShell Scripting, Trainer, Profi, Spezialist, Experte, Tipp, Schulung, Training, Kurs

Windows PowerShell 3.0 update-help funktioniert nicht

Auch in der finalen Version von PowerShell 3.0 funktioniert das CMDlet update-help nicht. Das ist nur zum Teil richtig, denn in der Englischen Version klappt es. Das liegt daran, dass die Deutschen Fassungen aktuell noch nicht veröffentlicht sind. Peter Kriegel postete kürzlich in Xing einen Workaround zu diesem Problem welches wenigstens die Hilfe in Englisch anzeigt:

Function Get-HelpUICulture {
 param (
 [String]$HelpSearchString,
 [String]$UICulture
 )
# UICulture merken, damit man Sie zurücksetzen kann
 $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture
 trap
 {
 [System.Threading.Thread]::CurrentThread.CurrentUICulture = $OldCulture
 }
# neue UICulture setzen
 [System.Threading.Thread]::CurrentThread.CurrentUICulture = $UICulture
# Hilfe in einer anderen Sprache abrufen
 # Get Help in another language
 Get-Help $HelpSearchString -Full
# alte UICulture zurücksetzen
 [System.Threading.Thread]::CurrentThread.CurrentUICulture = $OldCulture
 }
# Hilfe in Englisch abrufen:
 Get-HelpUICulture -HelpSearchString Get-Command -UICulture ‘en-US’

In seinem Blog findet man weitere Informationen und Links zu den neuen Hilfemechanismen.

Dynamische Variablennamen in Powershell

Wenn Sie einen Variablennamen selbst aus Varbiablen oder Textteilen zusammensetzen möchten, können Sie das mit Hilfe des Cmdlets new-variable wie folgt tun:

$a="Eins"
$b="Zwei"
new-variable -name ("Text"+$a+$b) -value 10 # Erstellt eine Variable mit dem Namen $TextEinsZwei und belegt diese mit dem Wert 10
$TextEinsZwei # Gibt den Inhalt der soeben dynamisch zusammengesetzten Variable zurück

So weit, so gut, aber wie lese ich die Variable nun wiederum dynamisch aus? Einen Tick umständlicher mittels Get-Variable Cmdlet:

(Get-Variable -name ("Text"+$a+$b)).Value

Cool, oder? 🙂

Zahlenformate in Powershell konvertieren

Um Zahlenformate in Powershell zu konvertieren kann man die Funktionen ToString der Klasse System.Convert von .NET verwenden. Das geht recht einfach:

$a=16 # Standardmäßig ist das erst einmal eine Dezimalzahl, also zu Basis 10
[System.Convert]::tostring($a,2) # Basis 2 macht daraus eine Binärzahl
[System.Convert]::tostring($a,8) # Basis 8 macht daraus eine Oktalzahl
[System.Convert]::tostring($a,16) # Basis 16 macht daraus eine Hexadezimalzahl

Mit Powershell aus dem AD Objekte abholen und nach nicht leeren Eigenschaften filtern

Es gibt viele Scriptbeispiele um mit Powershell Objekte aus dem Active-Directory abzuholen, bei denen eine bestimmte Eigenschaft ausgefüllt ist, oder nicht. Allerdings habe ich noch kein Script gefunden, dass ein Objekt abholt und dann davon alle leeren oder ausgefüllten Felder anzeigt. Das Problem liegt im Rückgabe-Wert des AD-Objekts. Dieses ist nämlich als Hash aufgehängt und läßt somit einen einfachen Zugriff auf den Wert zu. Erst mit Hilfe der .NET-Methode GetEnumerator() ist es möglich den Inhalt auszuwerten wie das folgende Beispiel anhand von get-aduser zeigt:

(get-aduser Administrator -properties *).getenumerator() | ? {$_.value} # zeigt alle ausgefüllten Eigenschaften
(get-aduser Administrator -properties *).getenumerator() | ? {!$_.value} # zeigt alle nicht ausgefüllten Eigenschaften

Befehle von Powershell Modulen eines anderen Rechners benutzen

Bei Windows XP und Server 2003 können Sie z. B. die Befehle aus dem Active-Directory Modul in der Powershell nicht benutzen. Es gibt jedoch eine Lösung, wie Sie die AD cmdlets (Befehle) ohne direkten Remotezugriff trotzdem in die Powershell Konsole von XP einbinden können:

$cred=get-credential
$session=new-pssession -computer 192.168.0.50 -cred $cred
invoke-command {import-module activedirectory} -session $session
import-pssession -session $session -module activedirectory

Im Script sind 2 Stellen fett markiert. Die erste Stelle enthält die IP-Adresse eines W2k8R2 DCs von dem die Modulbefehle importiert werden. Alternativ können Sie hier natürlich auch gerne den Namen einsetzen. Die zweite fett geschriebene Stelle am Schluß legt das zu importierende Modul fest. In diesem Beispiel ist es das AD Modul. Es kann natürlich auch ein X-beliebiges anderes Modul sein, dass auf der Remotemaschine allerdings instaloliert sein muss.

Powershell Variablen mit Befehlen belegen

Sie können in Powershell ganze Befehlsketten in Variablen ablegen, z. B. so:

$a=get-childitem

Dann wird allerdings zum Zeitpunkt der Variablendeklaration der Befehl ausgeführt und der Inhalt in der Variablen hinterlegt. Legt man also nun ein neues Verzeichnis an, ist dieses nicht in $a enthalten, da die Variable zuerst in Inhalt übergeben bekam und erst danach der neue Ordner erstellt wurde. Gibt man nun $a aus, fehlt der neu erstellte Ordner. Man kann allerdings auch Code in einer Variablen zum Zeitpunkt der Variablenabfrage ausführen lassen. Dazu müssen Sie den Code nur in geschweifte Klammern setzen und die Variable mit vorangestelltem Punkt abrufen. Also beispielsweise so:

$a={get-childitem}

Wenn Sie nun:

. $a

abrufen, wird der Inhalt der Variablen zum Zeitpunkt von der Angabe . $a als Code ausgeführt und enthält somit den aktuellen Stand des Verzeichnisses.

Auch interessant ist die Möglichkeit Befehle mit Variablen zu Befehlen zusammen zuschrauben.

$Verb="set"
&("$Verb-service")

Powershellscripte zur Verwaltung von Hyper-V

Wenn Sie kein SCVMM (System Center Virtual Machine Manager) Ihr eigen nennen wäre es doch trotzdem ganz nett einige Dinge davon selbst über Powershell zu machen. Microsoft hat eine Powershell Script Sammlung zur Hyper-V Verwaltung bereit gestellt. Damit können Sie u. a. virtuelle Maschinen und Snapshots bzw. Checkpoints erstellen und verwalten aber auch vhd’s konvertieren und noch vieles mehr. Die Sammlung wir stetig erweitert. Mit der Anleitung Powershell GUI-Programmierung für Dummies auf dieser Seite könnten Sie sich Ihren eigenen SCVMM zusammen bauen. Wenn ich einmal viel Langeweile habe mache ich das vielleicht für Sie ;-).

Hash Werte in Powershell

Eine Hash „Variable“ kann einen schon zur Verzweiflung treiben. Insbesondere, wenn man versucht Sie zu sortieren. Hat man beispielsweise folgenden Hash geschaffen:

$hash=@{A=1;C=3;B=2}

kommt bei der Eingabe von $hash folgendes heraus:

Name  Value
A     1
C     3
B     2

Einen weiteren Eintragt hinzufügen ist relativ einfach:

$hash+=@{D=4}

Aber wie bekomme ich den nun wieder raus? Dazu muss man eine Methode des Hashs verwenden:

$hash.remove("D")

Das hätte man nun gerne entweder nach Name oder Value sortiert. Die Lösung ist in der Methode GetEnumerator versteckt:

$hash.getenumerator() | sort Name

bzw.

$hash.getenumerator() | sort Value

löst dann Ihr Problem.

Soll ich bei IIS 7.x den Usermode-, oder den Kernelmodecache aktivieren, oder beide?

Die Antwort lautet wie üblich: Es kommt ganz darauf an! 😉

Fakten: Kernelmodecache ist schneller als Usermodecache, da ein Übergang in den Usermode immer viel Zeit kostet! Wenn ein Webserver im Normalfall ca. 550/s Anfragen schafft, so werden daraus bei aktiviertem Usermodecache ca. 650/s, bei Kernelcache 950/s. Aktiviert man bei geht er knapp über 1000/s. Allerdings hat man dafür nicht immer genug Speicherreserven.

Des Weiteren ist es davon abhängig wie IIS mit der jeweiligen Website arbeitet. Wird von der Webanwendung mehr im Usermode gearbeitet, ist es sinnvoller den Usermodecache zu aktivieren, arbeitet Sie mehr im Kernelmode, natürlich lieber den Kernelcache. Dazu muss man allerdings wissen, wie die jeweilige Webanwendung tickt. Das verrät der folgende Powershell-Befehl:

Get-WmiObject win32_process | ? {$_.caption -eq "w3wp.exe"} | select commandline,usermodetime,kernelmodetime | fl

Ausgabe sind alle Workerprozesse vom IIS. Dabei wird als erstes angezeigt, welcher Applicationpool sich hinter dem Workerprocess versteckt und danach folgen die Zeiten die er sich im User und im Kernelmode aufhält. Anhand dieser Zahlen kann man sehr schön festlegen, welche Cacheart die sinnvollere für den jeweiligen Applicationpool und den damit verknüpften Websites ist. Zugegeben ,man hätte das ganze noch viel eleganter Lösen können am besten gleich mit der automatischen Cachekonfiguration. Aber das war eben mal so auf die Schnelle Quick & Dirty eben ;-).

Datenbankzugriffe mit der Powershell

Leider bringt Powershell keine einfachen Cmdlets für den Zugriff auf Datenbanken mit. Die einzige Möglichkeit ist über ADO.NET zuzugreifen.

Vorbereitung

Je nach installierten Datenbankprovider gibt es verschiedene Möglichkeiten die unterschiedlichen Datenbanken (Access,MS-SQL,MY-SQL,Oracle,ODBC, etc…) zuzugreifen. Welche Provider überhaupt auf dem Computer registriert (z.B. Access installiert) sind können Sie mit:

[System.Data.Common.DBProviderFactories]::GetFactoryClasses() | select InvariantName

heraus finden. Falls Sie auf einen Microsoft SQL Server zugreifen möchten können Sie diese mittels:

[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()

auflisten lassen.

Access Datenbank anlegen bzw. erstellen

Eine neue Datenbankdatei in Access legt man wie folgt an:

$application = New-Object -ComObject Access.Application
$application.NewCurrentDataBase("C:\Pfad\Datei.mdb",10)
$application.CloseCurrentDataBase()
$application.Quit()

Tabelle in Access Datenbank anlegen bzw. erstellen

$connection = New-Object -ComObject ADODB.Connection
$connection.Open("Provider= Microsoft.Jet.OLEDB.4.0;Data Source=C:\Pfad\Datei.mdb" )
$Fields = "NamederSpalte1 Counter, NamederSpalte2 Date, NamederSpalte3 Integer, NamederSpalte4 Text"
$command = "Create Table GewünschterNamederTabelle `($Fields`)"
$connection.Execute($command)
$connection.Close()

Die fett gedruckten Passagen geben wieder Ihre persönlichen Anpassungen an.

1. Den Pfad zur Access Datenbankdatei

2. Die Spalten bzw. Felder die angelegt werden sollen. Erst den Namen der jeweiligen Spalte und danach gefolgt der Datentyp (Counter=Zähler, Date=Datum, Integer=Zahl, Text=Text) für die Spalte.

3. Name der Tabelle

Verbindungsaufbau und Tabelle auswählen

So stellen Sie eine Verbindung mit einer Datenbank her und öffnen eine Tabelle:

# Angaben zur Verbindung mit der Datenbank
$Verbindung="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Pfad zu Ihrer Datenbank\Datei.mdb;"
$SQL="Select * from tabelle order by Vorname"
# Tabelle aus der Datenbank in Variable ablegen
$DB=New-Object System.Data.OleDB.OleDbConnection($Verbindung)
# Öffnen der Tabelle
$DB.open()
"Tabelle: "+$DB.State
# Schließen der Tabelle
$DB.close()
"Tabelle: "+$DB.State
Dies ist ein Beispiel für den Zugriff auf eine Access-Datenbank. Die unterstrichenen Abschnitte müssen Sie entsprechend anpassen. Das order by Vorname können Sie auch komplett weg lassen. Dies dient lediglich zur Sortierung nach der Spalte Vorname. Möchten Sie eine statische Verbindung zu einer MS-SQL Datenbank herstellen, müssen die Fett dargestellten Bereiche wie folgt abgeändert werden:

$Verbindung=“Data Source=.\RelativePositionderDatenbankbzwderenName;Initialcatalog=Users;Integrated Security=True;“

$DB=New-Object System.Data.SqlClient.SqlConnection($Verbindung)

Möchten Sie eine dynamische Verbindung zu einer MS-SQL Datenbank herstellen, müssen die Fett dargestellten Bereiche wie folgt abgeändert werden:

$Verbindung="Data Source=.\RelativePositionderDatenbankbzwderenName;AttachDbFileName=C:\Pfad\Datenbank.mdf;Integrated Security=True;"
$DB=New-Object System.Data.SqlClient.SqlConnection($Verbindung)     # wie bei der statischen Verbindung
Daten aus einer Tabelle auslesen

Das folgende Script zeigt die Möglichkeit Daten aus der Datenbank auszulesen. Die fett markierten Stellen sind die dokumentierten Änderungen zum vorangegangenen Script.

# Angaben zur Verbindung mit der Datenbank
$Verbindung="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\db\test.mdb;"
$SQL="Select * from Services order by Zeit"
# Tabelle aus Datenbank in Variable ablegen
$DB=New-Object System.Data.OleDb.OleDbConnection($Verbindung)
# Öffnen der Tabelle aus der Datenbank
$DB.open()
"Tabelle: "+$DB.State
$Kommando=New-Object System.Data.OleDb.OleDbCommand($SQL,$Verbindung) # Erstellt ein Object für das SQL-Kommando
$Datenadapter=New-Object System.Data.OleDb.OleDbDataAdapter($Kommando)    # Wendet das SQL-Kommando auf die Datenbank an
$Dataset=New-Object System.Data.Dataset            # Erstellt eine Object um die Daten aufzunehmen
[Void] $Datenadapter.Fill($Dataset,"Services")   # Holt sich die Daten ab
# Schließen der Datenbankverbindung
$DB.close()
"Tabelle: "+$DB.State
$Dataset.Tables[0] | ft          # Gibt die Daten als Tabelle formatiert auf dem Bildschirm aus
Daten in eine Tabelle schreiben
$strDB="C:\Pfad\Datei.mdb"
$strTable="Tabellenname"
$objCon=New-Object -comobject ADODB.Connection
$objRecordSet=New-Object -comobject ADODB.Recordset
$objCon.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = $strDB")
$objRecordSet.Open("SELECT * FROM Services",$objCon)
$objRecordSet.Addnew()
$objRecordSet.Fields.item("Spalte1")="Inhalt"
$objRecordSet.Fields.item("Spalte2")="Inhalt"
# usw. - je nachdem wie viele Spalten Ihre Tabelle enthält
$objRecordSet.Update()
$objRecordSet.close()
$objCon.close()
Auch hier sind die von Ihnen anzupassenden Stellen wieder fett markiert.

Datensatz aus einer Tabelle löschen

Mit dem SQL-Kommando werden die zu löschenden Datensätze ausgewählt und anschließend durch die delete Methode gelöscht.

# Datenbankverbindung herstellen
$strDB="C:\db\Services.mdb"
$strTable="Services"
$objCon=New-Object -comobject ADODB.Connection
$objRecordSet=New-Object -comobject ADODB.Recordset
$objCon.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = $strDB")
# Durch den SQL-Befehl werden die zu löschenden Datensätze ausgewählt
$objRecordSet.Open("SELECT * FROM Services WHERE Dienstname='Appinfo'",$objCon,3,3)
# und dann darauf die Delete-Methode angewendet
$objRecordSet.delete()
# Datenbankverbindung lösen
$objRecordSet.close()
$objCon.close()
write-host
write-host "Datensatz gelöscht!"