Tag Archives: PowerShell

Easy monitoring of a folder with pswatch

pswatch is a easy to use little module for PowerShell to montitor a folder for changes.

You can choose if you want to monitor file additions, changes, renames or deletions. You can also monitor file sin subfolders.

Based on this monitoring you can further add scripting to automatically run other commands (see example on github).

I further customized the output to include the time of the occurrence of the event:

watch $env:Temp -includeDeleted | % { $_ | Add-Member ScriptProperty Time { Get-Date } -PassThru }

This adds a dynamic Time property which automatically updates itself when a new file change event is happening.

Advertisements
Tagged , ,

PowerShell Cmdlet zum Auslesen der Anmeldeinformationen aus der “Windows Anmeldeinformationsverwaltung”

Will man PowerShell-Operationen automatisieren benötigt man gelegentlich Anmeldeinformationen, welche dann irgendwie im Script zur Verfügung gestellt werden müssen. Normalerweise erledigt man dies über Get-Credential, welches allerdings immer eine Benutzerabfrage bedingt und in einem automatisch ablaufenden Script nutzlos ist.

Der einfachste (aber auch unsicherste) Weg ein Passwort auszulesen ist folgender:

$password = ConvertTo-SecureString <plaintextpassword> -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential <username>, $password

Dass dies nicht der bevorzugte Weg ist, sieht man am Umstand, dass der Paramter –Force angegeben werden muss, da ansonsten folgender Fehler ausgegeben wird: “Nur-Text-Eingaben können vom System nicht geschützt werden.”
Und wenn man (hoffentlich) etwas nachdenkt, ist dieses Verhalten auch vollkommen verständlich, denn wer will schon seine Passwörter im Klartext in einer Textdatei hinterlegen.
Auf der anderen Seite ist es doch immer wieder praktisch Logininformationen eines Systems zu ermitteln, indem man Dateien mit den klingenden Namen config.(php|asp|*), connection.(php|asp|*), usw.. durchstöbert. Zwinkerndes Smiley

Nun gibt es zwei Möglichkeiten Passwörter sicher zu hinterlegen

  1. Das Password wird verschlüsselt im Script selbst hinterlegt oder in einer Datei ausgelagert.
  2. Die Anmeldeinformationen werden im “Windows Credential Manager” (das ist die englische Bezeichnung für die Windows Anmeldeinformationsverwaltung) hinterlegt.

Methode 1: PowerShell “out-of-the-box”

Das Password wird in eine Date gespeichert:

// Die Login-Informationen werden abgefragt
$credential = Get-Credential
// Das Password wird in einer Datei gespeichert
$credential.Password | ConvertFrom-SecureString | Set-Content <file>

Das Auslesen des Passwortes wird dann folgendermaßen erledigt:

$password = Get-Content <file> | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential(<username>, $password)

Dieses PSCredential kann man dann den Cmdlets übergeben, welche die Anmeldeinforationen benötigen (z.B. Enter-PSSesssion <server> –Credential $credential).

Zu beachten ist, dass diese Methode nur funktioniert, wenn das Passwort auf demselben Computer ausgelesen wird, auf welchem es auch erstellt wurde, da für die Verschlüsselung/Entschlüsselung der MachineKey verwendet wird.

Damit die Methode über mehrere Computer funktioniert, kann man entweder den MachineKey der Computer angleichen (!) oder ConvertFrom-SecureString und ConvertTo-SecureString einen –Key oder -SecureKey übergeben, welcher dann wiederum sicher aufbewahrt werden sollte, und da man dies mit Methode 1 endlos wiederholen müsste, würde sich Methode 2 anbieten. Zwinkerndes Smiley

Methode 2: “Windows Anmeldeinformationsverwaltung”

Informationen zur Windows Anmeldeinformationverwaltung (englisch: Windows Credential Manager) findet man hier: http://windows.microsoft.com/de-DE/windows7/What-is-Credential-Manager

Hinterlegt werden diese Anmeldeinformationen über Systemsteuerung/Benutzerkonten/Anmeldeinformationsverwaltung. Dabei wird eine Internet- oder Netzwerkadresse (welche später als Schlüssel dient) das Login und das Passwort hinterlegt.

Leider bietet PowerShell keine Möglichkeiten diese Anmeldeinformationen nativ auszulesen. Aus diesem Grund habe ich ein PowerShell Cmdlet erstellt, welches diese Aufgabe übernimmt.

Die Datei findet man hier:

Die Verwendung gestaltet sich dann wie folgt:

$credential = .\Get-CredentialFromWindowsCredentialManager.ps1 <internet- oder netzwerkadresse>
Enter-PSSession <server> –Credential $credential

Die Anmeldeinformationen sind lokal und Benutzerbezogen, das heißt natürlich, sie sind bei Bedarf für ein weiteres Benutzerkonto oder auf einem weiteren Rechner ebenfalls im Windows Credential Manager des jeweiligen Benutzerkontos zu hinterlegen.

Tagged , , ,

Tapetenwechsel für die PowerShell Konsole

PowerShell ist schon eine coole Sache! Leider wird diese “Coolness” nicht über ihr Interface widergespiegelt, sondern es sind eher die inneren Werte die die PowerShell zu einem – für mich – absolut unverzichtbaren Werkzeug machen. Das gegenwärtige “Outfit” der PowerShell Konsole sieht folgendermaßen aus: Inspiriert von dem wohl zurzeit schicksten Editor – dicht gefolgt von VS11 natürlich 😉 – “Sublime Text 2“, habe ich mich dazu entschlossen die PowerShell neu einzukleiden: Ob das neue Styling nun den Geschmack des Lesers trifft liegt ehrlich gesagt außerhalb meines subjektiven Relevanzkorridors. Mir gefällts jedenfalls! 😉 Aber keine Sorge, die Farben können natürlich nach eigenem Gutdünken angepasst werden.

Und hier die Schritt für Schritt Anleitung

Zuerst muss man die Konsoleneigenschaften der PowerShell anpassen. Dies kann man mit folgendem Script erledigen.

Ich hab nicht alle Farben angepasst, nur die, die mir in der PowerShell bisher untergekommen sind.

Zu beachten ist, dass die Farben im HEX-Code anzugeben sind. Die ersten zwei Stellen können ignoriert werden (bezieht sich NICHT auf die Transparenz, wie man vielleicht vermuten würde). Allgemein ist das Muster nicht mit den #ARGB-Werten, wie man sie z.B. aus WPF/Silverlight her kennt, zu verwechseln. Die Farben sind nämlich als BGR-Werte zu definieren, dh. 0x00FF0000 ist nicht rot, sondern blau! Hat mich einige Zeit gekostet, bis ich endlich kapiert habe wie die Farbe aufgeschlüsselt ist…

Am Ende des Scripts kopiere ich nun alle Werte, so dass diese Einstellungen auch für die Verknüpfung selbst verwendet werden. Andernfalls greifen diese Einstellungen nur, wenn die PowerShell über ihren vollständigen Namen aufgerufen wird. Hier ist zu beachten, dass der Ordner gleich zu benennen ist, wie der Verknüpfungsname (im Startmenü). Allerdings ist diese Verknüpfung etwas widerspenstig. Sie definiert nämlich ein eigenes Farbschema und ignoriert unsere Einstellungen gnadenlos. Was hilft ist einfach die alte Verknüpfung zu löschen und eine neue Verknüpfung zu erstellen. Eventuell sollte man das Ausführungsverzeichnis auf %HOMEDRIVE%%HOMEPATH% festlegen, wie es vorher definiert war. Wie bereits gesagt, der Name ist wichtig, da sonst die Einstellungen wiederum nicht greifen. In meinem Beispiel verwende ich die Bezeichnung “Windows PowerShell”. Nun sollten die Einstellungen endlich Wirkung zeigen!

Kleine Zugabe

Um der PowerShell zusätzlich noch etwas Farbe zu verleihen habe ich schlussendlich noch den Prompt überschrieben. Dieses Code-Schnipsel kann im PowerShell-Profil hinterlegt werden, sodass es bei jedem Start geladen wird (eventuell kann man statt Green auch Blue hernehmen, was etwas mehr Kontrast zu den Warnmeldungen hat).

Hier nochmal die Ausgabe mit allen Farben:

Tagged ,

SharePoint 2010 Management Shell Ausgaben anpassen

Die “SharePoint 2010 Management Shell”, welches im Grunde eine PowerShell-Instanz ist, welche das PSSnapin mit dem Namen Microsoft.SharePoint.PowerShell geladen hat, ist extrem flexibel.

Ich könnte jetzt eine seitenweise Huldigung über die PowerShell schreiben, werde mich aber am Riemen reißen und nur einen der vielen Erweiterungspunkte der PowerShell ansprechen.

Die Erweiterungspunkte welche ich ansprechen möchte sind in der Hilfe von PowerShell dokumentiert.
Die beiden man-pages (wenn man die Linux-kompatible Bezeichnung für die Hilfe verwendet) nennen sich about_Types.ps1xml und about_Format.ps1xml:

man about_Types.ps1xml
man about_Format.ps1xml

Was mit diesen beiden XML-Dateien möglich ist, ist zum Einen das Typsystem der PowerShell zu erweitern (Types.ps1xml) und zum Anderen die Ausgabe auf dem Host zu beeinflussen (Format.ps1xml).

Types.ps1xml

Eines der wohl promintesten Beispiele für eine Erweiterung des Typsystems ist wohl das Count AliasProperty des Array-Objektes. .NET-Programmierer wissen, dass die Länge eines Arrays über das Length-Property ermittelt wird. Andere Collections benutzen dafür aber das Count-Property. Um diese Inkonsistenz zu vermeiden führt die PowerShell das Length AliasProperty ein.

Überall in der PowerShell findet man solche Typerweiterungen. Es gib verschiedenste Möglichkeiten einen Typ zu erweitern, AliasProperty ist die simpelste Möglichkeit und ist einfach ein Verweis auf ein bereits bestehendes Property (im Beispiel des Array.Count ist dies ein Verweis auf Array.Length). Es gibt aber auch komplexere, mit welchen Berechnungen möglich sind, wie z.B. das ScriptProperty. Weitere Informationen bitte about_Types.ps1xml entnehmen.

Format.ps1xml

Diese Datei ermöglicht die Ausgabe der Powershell auf dem Host zu beeinflussen. Ein einfaches Beispiel ist die Ausgabe des Verzeichnissinhaltes mit dir: da es sich im Grunde um .NET-Objekte handelt, welche zurückgegeben werden, muss der PowerShell mitgeteilt werden, wie diese schlussendlich dem Benutzer präsentiert werden sollen, da sonst jede einzelne Eigenschaft in einer Liste ausgegeben wird. Lesbarer (und auch vom DOS-Prompt her gewohnt) ist die Ausgabe mit einer eingeschränkten Anzahl von Eigenschaften und einer tabellarischen Ausgabe. Und genau hierfür sorgt die angesprochene Datei. Weitere Informationen bitte about_Format.ps1xmlentnehmen.

Und was bringt’s mir im Zusammenhang mit SharePoint?

Nun, SharePoint bietet leider eine sehr rudimentäre Implementierung dieser beiden Erweiterungspunkte. Zu finden sind die Formatdateien unter: %PROGRAMFILES%Common FilesMicrosoft SharedWeb Server Extensions14CONFIGPOWERSHELLFormat und die Typedateien unter: %PROGRAMFILES%Common FilesMicrosoft SharedWeb Server Extensions14CONFIGPOWERSHELLTypes.

Wie gesagt, diese Format- und Typdefinitionen sind recht rudimentär. Auf einem Produktivsystem sollten diese Definitionen aber ausreichen. Auf einem Entwicklersystem ist es aber manchmal bequemer, wenn man etwas mehr Informationen aus der PowerShell-Ausgabe erhält, als dies standardmäßig der Fall ist.

Weiter unten habe ich zwei Dateien angehängt, welche nach belieben angepasst werden können. Sie sollen lediglich als Ideengeber dienen.

Geladen werden die Dateien mittels den zwei Kommandos:

Update-TypeData -Prepend SharePoint.types.ps1xml
Update-FormatData -Prepend SharePoint.format.ps1xml

Diese beiden Befehle können im Profil hinterlegt werden, sodass sie beim Aufruf der PowerShell automatisch geladen werden.

Die Reihenfolge sollte wie oben sein (Type, dann Format), da die Ausgabe oftmals auf die definierten Typen verweist.

Falls nachträglich Änderungen an den beiden Dateien durchgeführt wurden, können die Definitionen über Update-TypeData und Update-FormatData neu geladen werden (ohne -Prepend).

Beispielausgabe

Standardausgabe von SharePoint:

Nach dem Laden der neuen Typ- und Ausgabedefinitionen:

Beispieldateien

Die Informationen, welche ausgegeben werden sollen, können für jeden Benutzer/Entwickler unterschiedlich sein, deshalb habe ich jeweils ein Beispiel-XML für die Formate und eines für die Types als Ausgangsdateien erstellt. Diese können dann selbst erweitert und angepasst werden:

SharePoint.format.ps1xml

<Configuration>
  <ViewDefinitions>
    <View>
      <Name>Microsoft.SharePoint.SPWeb</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPWeb</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Width>25</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>9</Width>
          </TableColumnHeader>
          <TableColumnHeader />
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>LastItemModifiedDate</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Language2</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Url</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
    <View>
      <Name>Microsoft.SharePoint.SPList</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPList</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader />
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>10</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>9</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>36</Width>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>LastItemModifiedDate</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>FieldCount</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>ItemCount</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>ID</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
    <View>
      <Name>Microsoft.SharePoint.SPListItem</Name>
      <ViewSelectedBy>
        <TypeName>Microsoft.SharePoint.SPListItem</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Width>8</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
            <Label>ContentType</Label>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>7</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>20</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
          <TableColumnHeader>
            <Width>19</Width>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>ID</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Title</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <ScriptBlock>$_.ContentType.Name</ScriptBlock>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Version</PropertyName>
                <Alignment>Right</Alignment>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Author</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Created</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Modified</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

SharePoint.types.ps1xml

<Types>
  <Type>
    <Name>Microsoft.SharePoint.SPWeb</Name>
    <Members>
      <AliasProperty>
        <Name>LCID</Name>
        <ReferencedMemberName>Language</ReferencedMemberName>
        <TypeName>System.Int32</TypeName>
      </AliasProperty>
      <ScriptProperty>
        <Name>Language2</Name>
        <GetScriptBlock>
          [System.Globalization.CultureInfo]::GetCultureInfo($this.LCID).Name
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
  <Type>
    <Name>Microsoft.SharePoint.SPList</Name>
    <Members>
      <AliasProperty>
        <Name>Url</Name>
        <ReferencedMemberName>DefaultViewUrl</ReferencedMemberName>
        <TypeName>System.Uri</TypeName>
      </AliasProperty>
      <ScriptProperty>
        <Name>FieldCount</Name>
        <GetScriptBlock>
          $this.Fields.Count
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
  <Type>
    <Name>Microsoft.SharePoint.SPListItem</Name>
    <Members>
      <ScriptProperty>
        <Name>Url</Name>
        <GetScriptBlock>
          $this["ServerUrl"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Author</Name>
        <GetScriptBlock>
          $this.Fields.GetFieldByInternalName("Author").GetFieldValueAsText($this["Author"])
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Created</Name>
        <GetScriptBlock>
          [DateTime]$this["Created"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Modified</Name>
        <GetScriptBlock>
          [DateTime]$this["Modified"]
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Version</Name>
        <GetScriptBlock>
          if ($this.Versions -and $this.Versions.Count -gt 0) {
          $this.Versions[0].VersionLabel
          }
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>FieldCount</Name>
        <GetScriptBlock>
          $this.Fields.Count
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>WorkflowCount</Name>
        <GetScriptBlock>
          if ($this.Workflows) { $this.Workflows.Count } else { 0 }
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>RunningWorkflowCount</Name>
        <GetScriptBlock>
          if ($this.Workflows -and $this.Workflows.Count -gt 0) {
          @($this.Workflows | ? { !$_.IsCompleted }).Count
          } else { 0 }
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>
Tagged , ,

PowerShell Script zum Konvertieren von .XLS-Dateien in .XLSX-Dateien

Eines Vorweg: Ich bin kein Freund davon alle alten Excel-Dateien auf Gedeih und Verderb in das “neue” OpenXML-Format zu konvertieren. Das alte binäre Format ist in den allermeisten Fälle noch gut genug, deshalb sollte von Datei zu Datei entschieden werden, ob sich eine Konvertierung auszahlt.

Mir fallen auf die Schnelle vier Gründe ein, wofür sich eine Konvertierung lohnt:

  • Es sollen die neuen Features von Excel verwendet werden. Dies ist schlicht und einfach nur mit dem neuen Dokumentformat möglich.
  • Es muss Speicherplatz gespart werden (das neue Format ist teilweise um die Hälfte platzsparender als das alte Binärformat).
  • Ein oder mehrere Dokumente sollen automatisiert werden. Diese Aufgabe ist über das OpenXML-SDK möglich, ohne dass das Office Paket installiert ist. Dies ist vor allem auf einem Server (z.B. SharePoint, Office 365) wichtig, da dort eine Installation des Office Paketes tunlichst vermieden werden sollte.
  • Es sollen die neuen Funktionen zur Gruppenzusammenarbeit genutzt werden, sodass mehrere Benutzer auf ein und dasselbe Excel-Dokument zugreifen können und dort Änderungen vornehmen (dies setzt einen SharePoint 2010 Server oder Office 365 voraus).

Da eine Konvertierung alter Dokumente nicht immer eine Entscheidung einer einzelnen Person ist und dafür nicht immer ein Praktikant zur Verfügung steht, der einem diese Aufgabe abnimmt (alte Datei aufmachen, Datei als .XLSX oder, falls ein Macro enthalten, als .XLSM abspeichern) habe ich ein PowerShell-Script geschrieben, welches diese Aufgabe erleichtert.

Beispiel 1

Direktes Konvertieren von Bericht.xls nach Bericht.xlsx:

.Convert-Xls2Xlsx.ps1 Bericht.xls Bericht.xlsx

Beispiel 2

Konvertiert mehrere Excel-Dateien in einem Ordner mit Unterordnern:

PS> ls D:ExcelFolder -Recurse -Include *.xls |
>> .Convert-Xls2Xlsx.ps1 -AutoConvert

Beispiel 2 konvertiert – bei vorhandenem AutoConvert-Parameter und dem Vorhandensein von Makros – die Datei automatisch in ein “macro enabled” OpendXML (Endung .XLSM). Die konvertierten Excel-Dateien werden in demselben Ordner wie die alten Excel-Dateien abgespeichert. Falls dies nicht gewünscht ist, kann auch ein -DestinationFolder angegeben werden.

Falls eine Datei mit demselben Namen vorhanden ist, kann man mit dem Parameter -Force das Überschreiben der Datei erzwingen.

Der Script-Code befindet sich auf bitbucket.org.

Tagged , ,