Benutzer-Werkzeuge

Webseiten-Werkzeuge


coding:powershell

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
coding:powershell [2025/02/27 13:03]
jango [Links]
coding:powershell [2025/04/14 12:55] (aktuell)
jango [Postfach]
Zeile 1: Zeile 1:
 +Powershell5 -> Net-Framework
 +Powershell7 -> .NET
 +
 <code powershell> <code powershell>
 $command = 'Get-ACL .\test' $command = 'Get-ACL .\test'
Zeile 149: Zeile 152:
 //Verbindung mit dem Active Directory herstellen: //Verbindung mit dem Active Directory herstellen:
 Import-Module ActiveDirectory Import-Module ActiveDirectory
 +
 +// Domäne abrufen
 +Get-ADDomain
  
 //Domänencontroller abrufen: //Domänencontroller abrufen:
Zeile 182: Zeile 188:
 </code> </code>
  
-__false__+====Gruppen====
  
 +<code powershell>
 +# Benutzergruppe erstellen:
 +New-ADGroup -Name "RODC Admins" -SamAccountName RODCAdmins -GroupCategory Security -GroupScope Global -DisplayName "RODC Administrators" -Path "CN=Users,DC=Fabrikam,DC=Com" -Description "Members of this group are RODC Administrators"
 +
 +# Benutzerkonto löschen:
 +Remove-ADGroup -Identity RODCAdmins
 +</code>
 ==== Organisationseinheiten (OU) ==== ==== Organisationseinheiten (OU) ====
  
Zeile 358: Zeile 371:
 Verwaltung einer [[:microsoft_exchange|Microsoft Exchange]] OnPremise Installation Verwaltung einer [[:microsoft_exchange|Microsoft Exchange]] OnPremise Installation
  
-<code powershell> +====Inbox Rules====
-Start-ExchangeManagementConsole+
  
 +<code>
 +Get-InboxRule -Mailbox "**********"
 +
 +Name                            Enabled Priority RuleIdentity
 +----                            ------- -------- ------------
 +Mails an Walter Friedrich und I True    1        17352165469276078081
 +</code>
 +
 +<code>
 +Remove-InboxRule -Mailbox "*****" -Identity 17352165469276078081
 +</code>
 +====Postfach====
 +<code powershell>
 // Postfach erstellen // Postfach erstellen
 New-Mailbox -UserPrincipalName user@domain.com -Alias user -Name "Vorname Nachname" -OrganizationalUnit "OU=Benutzer,DC=domain,DC=com" New-Mailbox -UserPrincipalName user@domain.com -Alias user -Name "Vorname Nachname" -OrganizationalUnit "OU=Benutzer,DC=domain,DC=com"
  
-//Berechtigungen für ein Postfach festlegen: +// Berechtigungen für ein Postfach anzeigen 
-Add-MailboxPermission -Identity user -User manager -AccessRights FullAccess,Editor,SendAs,SendOnBehalf,ReadPermission,WritePermission,DeleteItem,CreateItems,EditOwnedItems,EditAllItems,FolderVisible,FolderOwner+Get-MailboxPermission -Identity user 
 +Get-MailboxPermission -Identity user -User manager
  
-//Verteilergruppe erstellen+//Berechtigungen für ein Postfach festlegennur ein accessright auf einmal 
-New-DistributionGroup -Name "Vertriebsgruppe" -Alias "Vertrieb" -Members user1user2user3+Add-MailboxPermission -Identity user -User manager -AccessRights None,ChangeOwner,ChangePermission,DeleteItem,ExternalAccount,FullAccess,ReadPermission
  
-//Sicherheitsgruppe erstellen+// Berechtigungen für einen Postfach Kalender anzeigen(auf engl. Server Calendar) 
-New-DistributionGroup -Name "IT-Sicherheit" -Alias "ITSecurity" -SecurityEnabled $true+Get-MailboxFolderPermission -Identity user:\Kalender 
 +Get-MailboxFolderPermission -Identity user:\Kalender -User manager 
 + 
 +//Berechtigungen für ein Postfach-Kalender festlegen: nur ein access right auf einmal (evtl Calendar auf engl.) 
 +Add-MailboxFolderPermission -Identity user:\Kalender -User manager -AccessRights None,CreateItems,CreateSubfolders,DeleteAllItems,DeleteOwnedItems,EditAllItems,EditOwnedItems,FolderContact,FolderOwner,FolderVisible,ReadItems
  
 //Postfachdaten exportieren: //Postfachdaten exportieren:
Zeile 378: Zeile 408:
 //Postfachdaten importieren: //Postfachdaten importieren:
 New-MailboxImportRequest -Mailbox user -FilePath "\\Server\Share\user.pst" New-MailboxImportRequest -Mailbox user -FilePath "\\Server\Share\user.pst"
- 
 //E-Mail-Adresse zu einem Postfach hinzufügen: //E-Mail-Adresse zu einem Postfach hinzufügen:
 Set-Mailbox -Identity user -EmailAddresses @{add="neueadresse@domain.com"} Set-Mailbox -Identity user -EmailAddresses @{add="neueadresse@domain.com"}
Zeile 385: Zeile 414:
 Set-Mailbox -Identity user -EmailAddresses @{remove="alteadresse@domain.com"} Set-Mailbox -Identity user -EmailAddresses @{remove="alteadresse@domain.com"}
  
 +// AutoReply
 +Set-MailboxAutoReplyConfiguration -Identity User1 -AutoReplyState Enabled -InternalMessage <message> -ExternalMessage <message>
 +
 +Set-MailboxAutoReplyConfiguration -Identity User1 -AutoReplyState Scheduled -StartTime "7/10/2018 08:00:00" -EndTime "7/15/2018 17:00:00" -InternalMessage <message> -ExternalMessage <message>
 +
 +// Mailbox in PST exportieren
 +New-MailboxExportRequest -Mailbox <user> –FilePath <pst-file>
 +
 +// Datenbank Speicherort
 +Get-Mailbox -Identity User1 | Get-MailboxStatistics | Select DisplayName, ServerName, Database | Sort-Object DisplayName
 +Get-Mailbox | Get-MailboxStatistics | Select DisplayName, ServerName, Database | Sort-Object DisplayName
 +
 +
 +// Mailbox verschieben
 +New-MoveRequest -Identity <user> -TargetDatabase <newdatabase>
 +Get-MoveRequest | Get-MoveRequestStatistics | Select DisplayName, Status, PercentComplete
 +Remove-MoveRequest -Identity <user>
 +Get-RemoveRequest | Remove-MoveRequest
 +
 +
 +// Message Tracking
 +Get-MessageTrackingLog -sender User1 
 +Get-MessageTrackingLog -Recipients User1
 +Get-MessageTrackingLog -sender User1 -Recipients User2
 +Get-MessageTrackingLog -sender User1 -Start <startdate> -End <enddate>
 +</code>
 +
 +====Verteiler====
 +<code powershell>
 +//Verteilergruppe erstellen:
 +New-DistributionGroup -Name "Vertriebsgruppe" -Alias "Vertrieb" -Members user1, user2, user3
 +
 +//Sicherheitsgruppe erstellen:
 +New-DistributionGroup -Name "IT-Sicherheit" -Alias "ITSecurity" -SecurityEnabled $true
 +</code>
 +
 +====Datenbank====
 +
 +<code powershell>
 //Exchange-Datenbanken auflisten: //Exchange-Datenbanken auflisten:
 Get-MailboxDatabase Get-MailboxDatabase
Zeile 1621: Zeile 1689:
  
 } }
 +</code>
 +
 +=====Powershell Wissen=====
 +
 +<code powershell>
 +#region VS Code
 +
 +<#
 + 
 +Install-Module -Name PSScriptAnalyzer -Force -Confirm:$false
 + 
 +Erweiterungen
 +    - PowerShell
 +    - XML Tools
 +    - Material Icon Theme
 +    - Code Spell Checker
 +    - German - Code Spell Checker
 +    - OPTIONAL German Language Pack for Visual Studio Code
 +    - OPTIONAL Markdown All in One
 + 
 +* Type ? into the input field to get a list of available commands you can execute from here
 + 
 +* F1 > settings.json
 +{
 +    "workbench.colorTheme": "PowerShell ISE",
 +    "editor.tabCompletion": "on",
 +    "powershell.integratedConsole.focusConsoleOnExecute": false,
 +    "files.autoSave": "afterDelay",
 +    "terminal.integrated.fontSize": 12,
 +    "terminal.integrated.lineHeight": 0,
 +    "editor.mouseWheelZoom": true,
 +    "editor.minimap.enabled": false,
 +    "editor.renderWhitespace": "all",
 +}
 +#>
 +
 +#endregion
 +
 +#region Hotkeys (VS Code)
 +
 +# CTRL + 1 Wechsel vom Terminal zum Editor
 +# CTRL + + Zoom in
 +# CTRL + - Zoom out
 +# F1 Kommandozeile
 +# CTRL + Space Autovervollständigung öffnen
 +# F8 Akt. Zeile oder Selektion ausführen
 +# CTRL + S Akt. Datei speichern
 +# CTRL + K + 0 region einklappen
 +# CTRL + P Quickly open files.
 +# CTRL + F1 Online-Hilfe zum akt. CmdLet
 +# SHIFT + ALT + UP Copy line up
 +# SHIFT + ALT + DOWN Copy line down
 +# CTRL + T Go to Symbol in Workspace
 +# CTRL + ALT + J Snipping einfügen
 +# F5 PS1-Datei ausführen
 +
 +#endregion
 +
 +#region TOP 10 CmdLets
 +
 +Get-Member
 +Get-Help
 +Get-Command      
 +Compare-Object  
 +ForEach-Object  
 +Group-Object    
 +Measure-Object  
 +New-Object      
 +Select-Object   
 +Skip-Object     
 +Sort-Object     
 +Where-Object    
 +Show-Command
 +Out-GridView
 +
 +#endregion
 +
 +#region GRUNDLAGEN
 +#region > > > AGENDA < < <
 +
 +# PowerShell - Grundlagen
 +
 +Start-Process "https://www.gfu.net/seminare-schulungen-kurse/erweiterungen_sk60/powershell-aufbaukurs_s1388.html"
 +
 +# SCHULUNGSKERNZEIT: 9:00 - 16:30
 +# PAUSEN: 10:30, 12:00-13:00, 14:30
 +#
 +
 +## Grundlagen
 +# Was ist PowerShell? [x]
 +# Befehlskonzepte und Terminologie [x]
 +# Parsing und cmdlets [x]
 +# Pipelines und Befehle [x]
 +# Formatierung und Ausgabe [x]
 +# PowerShell Provider und Drives [x]
 +# PowerShell Remote einsetzen [x]
 +                                              
 +## Arbeiten mit Variablen
 +# Was sind Variablen? [x]
 +# Variablenmanagement [x]
 +# Basistypen [x]
 +# Schreibschutz und Konstanten [x]
 +                                              
 +## Operatoren und Ausdrücke
 +# Arithmetische Operatoren [x]
 +# Die Zuweisungsoperatoren [x]
 +# Vergleichsoperatoren [x]
 +# Operatoren zum Musterabgleich [x]
 +# Logische Operatoren [x]
 +                                              
 +## Erweiterte Operatoren und Variablen
 +# Array-Operatoren [x]
 +# Hashtables - Assoziative Arrays [x]
 +# Der PowerShell Format-Operator -F [x]
 +                                              
 +## Windows Objekte: COM und WMI
 +# COM in PowerShell verwenden [x]
 +# WMI und PowerShell [x]
 +                                              
 +## Verarbeitung von Texten, Dateien und XML
 +# Verarbeiten von unstrukturiertem Text [x]
 +# Dateiverarbeitung [x]
 +# XML-Verarbeitung [x]
 +
 +#endregion
 +#region Was ist PowerShell?
 +
 +# Die Windows PowerShell ist ein KOMMANDOZEILENINTERPRETER von Microsoft
 +C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -?
 +
 +# Die auf dem Microsoft .NET-Framework basierende PowerShell verbindet die aus Unix-Shells bekannte
 +# Philosophie von Pipes und Filtern mit dem Paradigma der objektorientierten Programmierung.
 +Get-Service | where Status -EQ Running | Get-Member
 +
 +# Objektorientiert und mit .NET erweiterbar
 +$file = Get-ChildItem C:\Windows\notepad.exe
 +$file.LastAccessTime
 +$file.Length
 +$file.CopyTo("c:\temp\notepad.exe")
 +$file | Add-Member -MemberType ScriptProperty -Name LengthKb -Value {$this.Length / 1KB}
 +$file.LengthKb
 +
 +# Unterstützt 32/64bit
 +C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
 +C:\Windows\syswow64\WindowsPowerShell\v1.0\powershell.exe
 +
 +# Enthält eine grafische Entwicklungsumgebung PowerShell ISE (Integrated Scripting Environment)
 +C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe
 +C:\Windows\syswow64\WindowsPowerShell\v1.0\powershell_ise.exe
 +
 +# Ab Windows 7 ist die PowerShell bereits vorinstalliert
 +
 +#region PowerShell 1.0
 +
 +# Markteinführung
 +
 +#endregion
 +
 +#region PowerShell 2.0
 +
 +# Remoting via RPC
 +Get-Help -Name * -Parameter ComputerName # Nur diese CmdLets können remote zugreifen
 +
 +#endregion
 +
 +#region PowerShell 3.0
 +
 +# Default in: Windows7
 +# Windows Server 2008 R2
 +
 +# Min. OS: >XP, >VISTA
 +
 +# Remoting via WinRM
 +# Workflows
 +# Scheduled[Jobs]
 +# Windows PowerShell Web Access
 +# Netzlaufwerke verbinden (New-PSDrive)
 +# Aktualisierbare Hilfe
 +# Web Cmdlet's
 +# ZZGL. Vereinfachte Syntax
 +Get-Process | where {$_.Handles -gt 500}
 +Get-Process | ?         Handles -GT 500
 +
 +#endregion
 +
 +#region PowerShell 4.0
 +
 +# DEFAULT: Windows 8, Windows Server 2012
 +
 +# Desired State of Configuration (DSC)
 +
 +#endregion
 +
 +#region PowerShell 5.0
 +
 +# Default OS: Windows 10, Windows Server 2016
 +
 +# Softwarepakete installieren
 +# Switch-Verwaltung
 +# OOP
 +#region Beispiele zu den Neuerungen der PowerShell Version 5.0
 +
 +# Alle Details zur 5.0 Version unter:
 +Get-Help about_Windows_PowerShell_5.0 -ShowWindow
 +Start-Process http://msdn.microsoft.com/de-de/powershell/scripting/whats-new/what-s-new-in-windows-powershell-50
 +
 +#region Get-ItemPropertyValue
 +
 +#NEU: Get-ItemPropertyValue
 +#FRÜHER:
 +(Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name ApplicationBase).ApplicationBase
 +#JETZT:
 +Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name ApplicationBase
 +
 +#endregion
 +
 +#region Einfache String-Operationen
 +"Hallo Welt" | ConvertFrom-String
 +"Hallo Welt" | ConvertFrom-String -Delimiter "ll"
 +"Hallo Welt" | ConvertFrom-String -PropertyNames FirstWord, SecondWord
 +
 +"Lee Holmes", "Steve Lee", "Jeffrey Snover" | Convert-String -Example "Bill Gates=Gates, B.","John Smith=Smith, J."
 +
 +"Hallo Welt" | Format-Hex
 +#endregion
 +
 +#region ZIP-Archive
 +
 +#
 +#Test-Daten erzeugen
 +#
 +New-Item -Path C:\temp\ZipMich -ItemType Directory -Force
 +1..1MB -join ";" | Set-Content -Path "C:\temp\ZipMich\LogFile1.txt" -Force
 +1..1MB -join ";" | Set-Content -Path "C:\temp\ZipMich\LogFile2.txt" -Force
 +
 +#
 +# Compress-Archive
 +#
 +"C:\temp\ZipMich" | Compress-Archive -DestinationPath C:\temp\ZipMich.zip -CompressionLevel Optimal -Force
 +Get-Help Compress-Archive -ShowWindow
 +
 +#
 +# Expand-Archive
 +#
 +"C:\temp\ZipMich.zip" | Expand-Archive -DestinationPath C:\temp\Backup -Force
 +Get-Help Expand-Archive -Full
 +
 +#endregion
 +
 +#region Software-Installation
 +
 +Set-ExecutionPolicy -ExecutionPolicy AllSigned
 +
 +Get-Command -Module PowerShellGet, PackageManagement
 +
 +Find-Package | Out-GridView
 +Install-Package -Name AKPT -Force
 +Get-Module -ListAvailable 
 +Get-Command * -Module AKPT
 +Get-AKAbout
 +Uninstall-Package -Name AKPT
 +
 +Register-PSRepository -Name "myNuGetSource" –SourceLocation "https://www.myget.org/F/powershellgetdemo/api/v2" -PublishLocation "https://www.myget.org/F/powershellgetdemo/api/v2/Packages" -InstallationPolicy Trusted
 +Get-PSRepository
 +Unregister-PSRepository -Name "myNuGetSource"
 +
 +#endregion
 +
 +#region Kryptographie
 +
 +Get-Command -Module Microsoft.PowerShell.Security 
 +
 +$MyCertInf = @"
 +[Version]
 +Signature = "$Windows NT$"
 + 
 +[Strings]
 +szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
 +szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"
 + 
 +[NewRequest]
 +Subject = "cn=me@example.com"
 +MachineKeySet = false
 +KeyLength = 2048
 +KeySpec = AT_KEYEXCHANGE
 +HashAlgorithm = Sha1
 +Exportable = true
 +RequestType = Cert
 + 
 +KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE"
 +ValidityPeriod = "Years"
 +ValidityPeriodUnits = "1000"
 + 
 +[Extensions]
 +2.5.29.37="{text}1.3.6.1.4.1.311.80.1"
 +"@
 +Set-Content -Path C:\temp\MyCert.inf -Value $MyCertInf
 +CertReq -new C:\temp\MyCert.inf C:\temp\MyCert.cer
 +$cert = gci Cert:\CurrentUser\My | ? Subject -EQ "cn=me@example.com"
 +
 +$crypt = "Hallo Welt" | Protect-CmsMessage -To $cert
 +Unprotect-CmsMessage -Content $crypt -To $cert
 +
 +#endregion
 +
 +#region OOP
 +
 +Get-Help about_Classes -ShowWindow
 +
 +enum Farbe
 +{
 +    Blau
 +    Grün
 +    Rot
 +}
 +$meineFarbe = [Farbe]::Grün
 +$meineFarbe 
 +
 +class Auto
 +{
 +    [Farbe]$Farbe
 +    $PS
 +}
 +
 +$meinAuto = New-Object -TypeName Auto
 +
 +$meinAuto.Farbe = [Farbe]::Grün
 +$meinAuto.PS = 100
 +$meinAuto | Get-Member
 +
 +#endregion
 +
 +#region Weitere neue CmdLetds
 +
 +Set-Clipboard -Value "Hallo Köln!"
 +Get-Clipboard
 +
 +Clear-RecycleBin -DriveLetter c: -Confirm:$false
 +
 +New-TemporaryFile
 +
 +New-Guid
 +
 +# symbolischer Verknüpfungen
 +New-Item -ItemType SymbolicLink -Name MySymLinkDir -Target $pshome 
 +
 +# -Depth 2
 +Get-ChildItem c:\ -Recurse -Depth 2 -Force
 +
 +#endregion
 +
 +#region DataCenter Abstraction Layer (DAL)
 +
 +<#
 +    Mit dieser Technologie können Sie direkt auf bestimmte
 +    Netzwerkkomponenten wie Switches und Router zugreifen.
 + 
 +    Dazu muss die Hardware diese Technik aber auch unterstützen.
 +    In diesem Bereich spielen vor allem
 +    Cisco und Huawei eine wichtige Rolle.
 +#>
 +
 +$Session = New-CimSession -ComputerName "NetworkSwitch08"
 +Get-NetworkSwitchFeature -CimSession $Session
 +
 +<#
 +    Name IsEnabled InstanceID PSComputerName
 +    ---- --------- ---------- --------------
 +    SSH True Contoso:Feature:2 10.19.26.49
 +    Tacacs True Contoso:Feature:3 10.19.26.49
 +    BGP False Contoso:Feature:4 10.19.26.49
 +    VLAN True Contoso:Feature:5 10.19.26.49
 +    LACP True Contoso:Feature:6 10.19.26.49
 +    DHCP False Contoso:Feature:7 10.19.26.49
 +    LLDP True Contoso:Feature:8 10.19.26.49
 +#>
 +
 +Get-Help Get-NetworkSwitchFeature -Full
 +
 +Get-Command -Module NetworkSwitchManager | Out-GridView
 +
 +#endregion
 +
 +#region Open Data Protocol
 +
 +<# Das Open Data Protocol, kurz OData ist ein von Microsoft veröffentlichtes HTTP-basiertes Protokoll
 +   für den Datenzugriff zwischen kompatiblen Softwaresystemen. Aufbauend auf älteren Protokollen wie
 +   ODBC und JDBC kann OData u.a. innerhalb von Cloud-Diensten (Azure), MySQL, Java und Rails
 +   eingebunden werden und ist in der Lage, in der Client-Server-Kommunikation eine einheitliche Semantik
 +   für den Datenaustausch zur Verfügung zu stellen.
 +#>
 +
 +Export-ODataEndpointProxy -Uri 'http://services.odata.org/v3/(S(snyobsk1hhutkb2yulwldgf1))/odata/odata.svc' `
 +                          -MetadataUri 'http://services.odata.org/v3/(S(snyobsk1hhutkb2yulwldgf1))/odata/odata.svc/$metadata' `
 +                          -AllowUnsecureConnection `
 +                          -OutputModule C:\Temp\GeneratedScript.psm1 `
 +                          -ResourceNameMapping @{Products = 'Merchandise'}
 +
 +#endregion
 +
 +#region Optimierte Unterstützung für 'Desired State Configuration' (DSC)
 +
 +<#
 +    Weitere Neuerungen in der PowerShell betreffen die mit der
 +    PowerShell 4.0 eingeführte Technologie Desired State Configuration (DSC).
 +     
 +    Hauptsächlich gibt es neue Optionen um festzulegen auf wievielen Computern
 +    gleichzeitig die Änderungen implementiert werden sollen.
 +      
 +    Mit dem Modul 'PowerShellGet' können Sie DSC-Ressourcen in der
 +    'PowerShell Resource Gallery' nutzen, installieren oder hochladen.
 +#>
 +
 +#endregion
 +
 +#endregion
 +
 +#endregion
 +
 +# Welche Version ist installiert?
 +Get-Host
 +
 +#endregion
 +#region Befehlskonzepte und Terminologie
 +
 +#region CmdLets (Verb-Substantiv)
 +# CmdLets-Quellen können sein *.dll (CmdLet) oder Funktionen (function)
 +Test-NetConnection 192.168.50.10
 +Get-Process
 +Get-Verb                  # Welche Verben gibt es?
 +Get-Command -Noun Process # Was kann ich mit Process machen?
 +Get-Command -Verb Get     # Welche Informationen können noch ermittelt werden?
 +# CmdLets sind keine EXE-Dateien, sie sind *.dll oder in *.ps1 definiert
 +# Mögliche CmdLets werden durch PowerShell-Module hinzugefügt, z.B. AD-CmdLets finden
 +Get-Module -ListAvailable
 +Get-Command -Module ActiveDirectory
 +Get-ADUser
 +#endregion
 +#region Alias
 +
 +ls  *.txt # (Alias für Get-ChildItem)
 +dir *.txt # (Alias für Get-ChildItem)
 +gci *.txt # (Alias für Get-ChildItem)
 +
 +Get-Command -Name dir
 +Get-Alias -Name ls                  # ls ist ein Alias für?
 +Get-Alias -Definition Get-ChildItem # Gibt es ein Alias für Get-ChildItem?
 +
 +# Console => Alias nutzen !
 +# PS-Skripte => Alias NICHT nutzen !
 +
 +#endregion
 +#region Pipelining
 +
 +Get-Service | Out-GridView
 +ls | Out-GridView
 +Get-Process *notepad* | Out-GridView -OutputMode Single | Stop-Process
 +
 +#endregion
 +#region Filtering
 +
 +ls -Path *.txt                        # Nicht alle Parameter unterstützen Wildcard!
 +ls | Where-Object Name -Like "*.txt"  # so geht filtern immer!
 +
 +#endregion
 +#region PowerShell-Providers/-Drives
 +
 +ls c:        # C: => PowerShell-Drive (C: d:, etc. => PowerShell-Provider FileSystem)
 +ls hkcu:     # hkcu: => PowerShell-Drive (hkcu:, hklm:, etc. => PowerShell-Provider Registry)
 +ls env:
 +# Weitere PS-Provider: Zertifikate, Exchange, ADS, SharePoint, VMWare, ...
 +Get-PSProvider
 +Get-PSDrive  
 +
 +#endregion
 +#region Remoting
 +
 +Get-Process -ComputerName 192.168.50.41
 +
 +# .NET Framework
 +$sw = New-Object -TypeName System.Diagnostics.Stopwatch  # Erzeugt ein Stopuhr-Objekt
 +$sw.Start()
 +$sw.Stop()
 +$sw
 +
 +#endregion
 +#region Zugriff auf WMI
 +
 +Get-WmiObject -Class Win32_Product
 +Get-WmiObject -Class Win32_Processor
 +Get-WmiObject -List | Measure-Object
 +
 +#endregion
 +#region Erweiterbar durch Module
 +
 +Get-Module -ListAvailable
 +Get-Command -Module ActiveDirectory # Informationen zu einem AD-Computer über: Get-ADComputer ...
 +Find-Module * # Ab PowerShell 5.0
 +
 +#endregion
 +
 +#endregion
 +#region PowerShell Parsing
 +
 +# Ein PowerShell Kommando wird bei parsen in token aufgeteillt und analysiert:
 +Write-Host Buch # Token Write-Host und Token Buch
 +
 +# Token werden wie folgt analysiert:
 +# Example Mode Result
 +# ------------------ ---------- ----------------
 +  2+2                # Expression 4 (integer)
 +  Write-Output 2+2   # Argument "2+2" (string)
 +  Write-Output (2+2) # Expression 4 (integer)
 +  $a = 2+2           # Expression $a = 4 (integer)
 +  Write-Output $a    # Expression 4 (integer)
 +  Write-Output $a/H  # Argument "4/H" (string)
 +  Write-Output !1    # Argument "!1" (string)
 +  Write-Output (!1)  # expression False (Boolean)
 +  Write-Output (2)   # expression 2 (integer)
 +
 +# STOP PARSING Möglichkeiten:
 +icacls X:\VMS /grant Dom\HVAdmin:(CI)(OI)F       # <= Problem
 +icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F   # <= Lösung 1
 +# Lösung 2: Stop das parsing nach --% =>
 +icacls X:\VMS --% /grant Dom\HVAdmin:(CI)(OI)F
 +
 +#
 +# Weitere Schreibweisen:
 +#
 +
 +$true                                       # Boolean
 +$false                                      # Boolean
 +10                                          # Integer
 +10.5                                        # Double
 +'Hallo Köln $PSCulture'                     # String '
 +"PowerShell-Kultur $PSCulture"              # String "
 +"Köln", "München", "Berlin", "Stuttgart"    # Array ,
 +(ping.exe 127.0.0.1)[8]                     # Array-Element [ ]
 +(tnc 192.168.50.10).PingSucceeded           # Ausdruck-Auswerten () zzgl. Eigenschaft des Ausw.-Objektes
 +(Get-Process -Name notepad).Kill()          # Aufruf von Methoden
 +Get-Process; Get-Command                    # Abschluß einer Befehlskette mit ;
 +[datetime]"2016-12-31"                      # DateTime [typenname]Objekt => Konvertierung
 +[DateTime]::Today                           # :: Ruft STATISCHE Member eines Objektes ab
 +
 +#endregion
 +#region PowerShell Console
 +
 +# -> Mehrzeilig schreiben = Ausdrücke in Klammern "()" setzen
 +    
 +# Tastaturbefehle (<= 4.0)
 +# TAB ...................... Befehlszeilenergänzung
 +# STRG + C ................. Abbruch
 +# PFEIL-OBEN/-UNTEN ........ Blättert im Befehls-Cache
 +# MARKIERUNG + ENTER ....... Kopiert die Markierung in die Zwischenablage
 +# RECHTS-KLICK ............. Fügt dir Zwischenablage ein
 +
 +# Tastaturbefehle (>= 5.0)
 +# STRG + C ................. Kopieren
 +# STRG + V ................. Einfügen
 +
 +Get-History
 +$MaximumHistoryCount = 50 # Default: 4096
 +
 +#region Konsolen-Ein-/Ausgabe protokollieren
 +
 +Start-Transcript -Path C:\Temp\PowerShellConsole.log
 +Get-Process | Stop-Process -Force -WhatIf
 +Remove-Item c:\ -Recurse -Force -WhatIf
 +Stop-Transcript
 +Get-Content -Path C:\Temp\PowerShellConsole.log
 +
 +#endregion
 +
 +#region max. Anzeige von Enumerationen (-1 ohne Grenze, Default: 4)
 +Get-Command -Name ForEach-Object | select -First 1 | fl * # Siehe Eigenschaft: Parameters
 +$FormatEnumerationLimit = 4
 +Get-Command -Name ForEach-Object | select -First 1 | fl * # Siehe Eigenschaft: Parameters
 +#endregion
 +
 +#endregion
 +#region Integrated Scripting Environment (ISE)
 +
 +# * = Noch nicht gespeichert
 +# STRG + S = Speichern
 +# F8 = Führt die aktuelle Auswahl aus bzw. die Course-Zeile
 +# F5 = Führt das ganze Skript aus
 +# TAB = Auswahl aus IntelliScense bestätigen
 +# STRG + LEER = öffnet IntelliScense
 +# STRG + M = Auf-/Zuklappen von Region und Kommentaren
 +# STRG + J = Snippets ausfüllen
 +
 +# Siehe auch:
 +# ISE-Menü "Add-Ons"
 +Get-Help about_PowerShell_Ise.exe
 +Get-Help about_Windows_PowerShell_ISE
 +
 +#endregion
 +#region PowerShell-Hilfe (Get-Help)
 +
 +#region Hilfe installieren und aktuallisieren
 +
 +Update-Help -Module * -UICulture en-US, de-DE -Force
 +
 +# Für Computer ohne Internet-Zugang
 +
 +Save-Help -DestinationPath C:\temp\PSHelpFiles -Module *
 +Update-Help -Module * -SourcePath C:\temp\PSHelpFiles
 +
 +#endregion
 +#region Hilfe zu CmdLet's
 +
 +Get-Process -?
 +Get-Help Get-Process
 +Get-Help Get-Process -Full
 +Get-Help Get-Process -ShowWindow # oder F1 in der ISE
 +Get-Help Get-Process -Online     # oder F1 in der ISE per MENÜ:\Tools\Optionen\...
 +
 +# SYNTAX-Block:
 +# -Name <String[]> => String-Array
 +# <CommonParameters> => Verweis auf CommonParameters
 +# [] => Optional
 +# [-FileVersionInfo] => Switch-Parameter
 +#
 +# Parameter-Beschreibung (z.B. Id):
 +# Erforderlich? true oder: false
 +# Position? named oder: 1, 2, 3, 4, 5, etc.
 +# Standardwert none oder: ......
 +# Pipelineeingaben akzeptieren? True (ByPropertyName) oder: False, True (ByValue)
 +# Platzhalterzeichen akzeptieren? false oder: true
 +
 +#endregion
 +#region Hilfe zur/um die PowerShell (about-Seiten)
 +
 +Get-Help about_*
 +Get-Help about_wildcards -ShowWindow
 +Get-Help about_if -ShowWindow
 +Get-Help about_CommonParameters -ShowWindow # Enthält infos von Parameter die für alle CmdLet's gelten
 +
 +Get-Process | Stop-Process -WhatIf
 +Get-Process -FileVersionInfo -ErrorAction SilentlyContinue
 +Get-Process -FileVersionInfo -ErrorAction Stop
 +Get-Process | Stop-Process -Confirm
 +
 +#endregion
 +#region Hilfe erstellen
 +
 +
 +# HILFE-ARTEN
 +# Cmdlet => The Help topics that describe cmdlets in a module are XML files that use the command help schema
 +# Provider => The Help topics that describe providers in a module are XML files that use the provider help schema.
 +# Function => The Help topics that describe functions in a module can be XML files that use the command help schema or comment-based Help topics within the function, or the script or script module
 +# Script => The Help topics that describe scripts in a module can be XML files that use the command help schema or comment-based Help topics in the script or script module.
 +# Conceptual ("About") => You can use a conceptual ("about") Help topic to describe the module and its members and to explain how the members can be used together to perform tasks. Conceptual Help topics are text files with Unicode (UTF-8) encoding. The file name must use the about_<name>.help.txt format, such as about_MyModule.help.txt. By default, Windows PowerShell includes over 100 of these conceptual About Help topics, and they are formatted like the following example.
 +
 +# <ModulePath>
 +# \Mymodule
 +# \en-US
 +# \about_SampleModule.help.txt
 +# \SampleModule.dll-help.xml
 +# \fr-FR
 +# \about_SampleModule.help.txt
 +# \SampleModule.dll-help.xml
 +
 +# Supporting Updatable Help
 +Start-Process "https://msdn.microsoft.com/de-de/library/hh852754(v=vs.85).aspx"
 +
 +# Supporting Online Help
 +Start-Process "https://msdn.microsoft.com/de-de/library/hh852737(v=vs.85).aspx"
 +
 +# PowerShell Cmdlet Help Editor
 +Start-Process "https://pscmdlethelpeditor.codeplex.com"
 +
 +#endregion
 +
 +# WICHTIG Get-Help intensiv nutzen
 +# WICHTIG about-Seiten (wiedeholt) lesen
 +
 +#endregion
 +#region Mögliche PowerShell-Kommandos (Get-Command, Get-Verb)
 +
 +# => Get-Command ruf eine Übersicht der möglichen PS-Kommandos ab.
 +# => Dieser Funktionsumfang hängt von den installierten Modulen ab.
 +# => Default-Anzeige von: CmdLets, Alias, Funktionen, PSDrives, Anwendungen (*.exe)
 +# => Vor PS3.0 werden nur CmdLets angezeigt von geladen Modulen
 +
 +Get-Command * -CommandType All
 +Get-Command * -CommandType Cmdlet
 +Get-Command -Name *computer* -CommandType All
 +
 +#
 +# Verb-Substantiv
 +#
 +
 +Get-Command -Noun Computer
 +Get-Verb
 +Get-Command -Verb Stop
 +Get-Command -Verb Optimize
 +
 +# ÜBUNG: Wie lautet das CmdLet um eine Netzwerk-Karte einzuschalten/auszuchalten bzw. neustarten?
 +# Und wie haben Sie das CmdLet gefunden?
 +
 +#
 +# Kommandos nach Modulen finden
 +#
 +
 +Get-Module -ListAvailable
 +Get-Command -Module NetTCPIP
 +Get-Help Test-NetConnection -ShowWindow
 +Test-NetConnection 192.168.50.10
 +
 +# ÜBUNG: Wie heißt das CmdLet um eine IP-Adresse zu ändern?
 +# Und wie haben Sie das CmdLet gefunden?
 +
 +#endregion
 +#region Aliase für CmdLet's (*-Alias)
 +
 +Get-Alias
 +Get-Alias ls
 +Get-Alias -Definition Get-ChildItem
 +Get-Command -CommandType Alias
 +Get-Alias -Definition Test-NetConnection
 +Get-Alias tc
 +Get-Command -Noun alias
 +Get-Help New-Alias -ShowWindow
 +New-Alias ping Test-NetConnection
 +ping
 +
 +# Nutzen
 +Get-Process | Where-Object Company -like "Microsoft*" | Sort-Object Name | Format-Table Name, Company
 +gp | ? Company -like "Microsoft*" | sort Name | ft Name, Company
 +
 +#endregion
 +#region Pipeline (|)
 +
 +# => Windows PowerShell überträgt über die Pipeline KEINE TEXTE zwischen den Befehlen,
 +# es werden immer OBJEKT über die Pipeline übertragen.
 +# => 1. CmdLet liefert das erste Objekt just-in-time an das 2. CmdLet
 +# => Pipeline-Objekte werden an den Parameter des 2. CmdLet's gebunden,
 +# dass Pipeline-Eingabe (byValue) zulässt (Siehe Get-Help)
 +
 +# Zum vertiefen & verstehen:
 +Trace-Command -PSHost -Name ParameterBinding -Expression {Get-Process winlogon | Format-Wide} 
 +
 +#
 +# Voarb analysieren (Wo bin ich & wo will ich hin?)
 +#
 +
 +Get-Process 
 +Get-Process | Get-Member
 +Get-Process | Format-Table -Property *
 +Get-Process | Format-Table -pro Name, Company
 +Get-Process | Format-List *
 +
 +Get-Process | Where-Object Company -like "Microsoft*" | Sort-Object Name | Format-Table Name,Company   # Endgültige Ausgabe z.B. zum schreiben in eine Datei
 +Get-Process | Where-Object Company -like "Microsoft*" | Sort-Object Name | Select-Object Name,Company  # Zum Weiterverarbeiten
 +
 +Get-Process |                                   # Nach der P. sind Umbrüche erlaubt
 +    Where-Object Company -like "Microsoft*" |   # Nach der P. sind Umbrüche erlaubt
 +    Sort-Object Name |                          # Nach der P. sind Umbrüche erlaubt
 +    Select-Object Name,Company |                # Nach der P. sind Umbrüche erlaubt
 +    Out-File c:\temp\Process.txt -Force
 +
 +
 +Get-Process | Where-Object Company -like `
 +    "Microsoft*" | Sort-Object Name | Select-Object Name, Company | Out-File c:\temp\PSTest\Process.txt -Force
 +
 +(Get-Process | Where-Object Company -like "Microsoft*").TotalProcessorTime.TotalSeconds | Measure-Object -Average -Sum -Maximum -Minimum
 +
 +#
 +# Weitere Beispiele:
 +#
 +
 +Get-ChildItem C:\ -Recurse | more # Besser nicht benutzten, lieber so:
 +Get-ChildItem C:\ -Recurse | Out-Host -Paging
 +
 +"192.168.50.10", "192.168.50.11", "192.168.50.12" | ForEach-Object -Process {tnc $_}
 +
 +
 +((ping.exe 127.0.0.1) | Select-String -Pattern 'Verloren = 0' | Measure-Object).Count -eq 1 
 +
 +
 +# ÜBUNG (Pipeline1): Anzeige der Gesamtbelegung im RAM aller Microsoft-Prozesse in MegaByte
 +
 +# ÜBUNG (Pipeline2): Welches ist die älteste Datei im C:\Windows-Order?
 +# ls, Get-Member, Format-List *, Sort-Object, Select-Object
 +
 +#endregion
 +#region Rückgabe-Objekte managen (*-Object)
 +
 +#
 +# Übersicht
 +#
 +
 +Get-Command -Noun Object -Module Microsoft.PowerShell.* | Out-GridView
 +
 +Where-Object   # Filtern (Zeile)
 +Select-Object  # Filtern (Spalten)
 +Sort-Object    # Sortieren
 +Group-Object   # Gruppieren
 +Measure-Object # Messen
 +Compare-Object # Vergleichen
 +ForEach-Object # Schleife
 +New-Object     # Erstellen
 +Tee-Object     # Verzweigen & loggen
 +
 +#region Where-Object (Siehe auch region Operatoren (Vergleichso.))
 +
 +# Einfache Schreibweise erst seit PS3.0
 +Get-Process | Where-Object -Property Company -Like -Value "Microsoft*"
 +Get-Process | where        -Property Company -Like -Value "Microsoft*"
 +Get-Process | ?            -Property Company -Like -Value "Microsoft*"
 +Get-Process | ?                      Company -Like        "Microsoft*"
 +
 +# UND
 +Get-Process | ? Company -Like "Microsoft*" | ? WorkingSet64 -GT 1000 # UND-Verknüpfung
 +
 +# ODER: Ausführliche Schreibweise seit PS1.0
 +Get-Process | ? -FilterScript {$_.Company -like "Microsoft*" -or $_.Company -like "Adobe*"} | ft Name, Company
 +Get-Process | ?               {$_.Company -like "Microsoft*" -or $_.Company -like "Adobe*"} | ft Name, Company
 +
 +# Die Variable $_ ist die Laufzeit-Variabe in der Pipeline und stellt das übergebene Objekt dar!
 +
 +#endregion
 +
 +#region ForEach-Object
 +
 +31,32,33,34,35,36 | ForEach-Object -Process {"192.168.50." + $_} 
 +31,32,33,34,35,36 | ForEach-Object {"192.168.50." + $_}
 +31,32,33,34,35,36 | foreach {"192.168.50." + $_}
 +31,32,33,34,35,36 | % {"192.168.50." + $_}
 +
 +31,32,33,34,35,36 | % {"TNC ausführen für"; "192.168.50." + $_}
 +31,32,33,34,35,36 | % {"TNC ausführen für"
 +                      "192.168.50." + $_}
 +31,32,33,34,35,36 | % {
 +    "TNC ausführen für"
 +     "192.168.50." + $_
 +}
 +
 +
 +Get-ChildItem C:\Windows\Logs -Fo -File -ea SilentlyContinue -Re | 
 +    ? Extension -EQ ".log"
 +    Get-Content -ea SilentlyContinue | 
 +    Select-String -Pattern "error"
 +
 +# Problem: Es fehlt die Information des Dateinamens :-(
 +# LÖSUNG: ForEach-Object
 +
 +Get-ChildItem C:\Windows\Logs -Force -File -ErrorAction SilentlyContinue -Recurse | 
 +   Where-Object -Property Extension -EQ -Value ".log"
 +   ForEach-Object -Process { 
 +        "#####################################" + $_.FullName # Ausgabe
 +        $_ | Get-Content | Select-String -Pattern "error" # Ausgabe des Dateiinhaltes die das Wort "error" enthalten
 +      }
 +
 +# Beispiele:
 +
 +Get-EventLog -LogName System -Newest 1000 | 
 +    ForEach-Object -Begin {Get-Date} `
 +                   -Process {Out-File -Filepath Events.txt -Append -InputObject $_.Message} `
 +                   -End {Get-Date}
 +
 +#endregion
 +
 +#region New-Object
 +
 +$o1 = New-Object -TypeName System.Windows.Forms.Form # Erstellt ein Objekt aus der .NET-Framework-Klasse 'Form'
 +$o1.ShowDialog() # .NET Wissen
 +
 +$o2 = New-Object -ComObject "Excel.Application"
 +$o2.Visible = $true # VBA for Application
 +
 +#endregion
 +
 +#region Tee-Object
 +
 +Get-Process | Stop-Process -WhatIf
 +Get-Process | Tee-Object -FilePath c:\temp\procs.txt | Stop-Process -WhatIf
 +Get-Process | Stop-Process -WhatIf | Tee-Object -FilePath c:\temp\procs.txt 
 +
 +#endregion
 +
 +#region Compare-Object
 +
 +Compare-Object -DifferenceObject 1 -ReferenceObject 2
 +Compare-Object -DifferenceObject 1 -ReferenceObject 1
 +
 +$a = "Hallo Köln", "Hallo München"
 +$b = "Hallo Köln", "Hallo Stuttgart"
 +Compare-Object -DifferenceObject $b -ReferenceObject $a
 +
 +#endregion
 +
 +#region Group-Object (Gruppiert zu Objektgruppen)
 +
 +Get-Service | Group-Object -Property Status | Get-Member
 +(Get-Service | Group-Object -Property Status -AsHashTable -AsString).Running
 +(Get-Service | Group-Object -Property Status).Group
 +
 +Get-Service | Sort-Object Status | Format-Table -GroupBy Status # Gruppiert die Ausgabe
 +
 +#endregion
 +
 +#endregion
 +#region Mit PowerShell-Objekten umgehen (Get-Member)
 +
 +# Alle CmdLets liefern Objekte zurück die intensiv ausgewertet werden können / müssen
 +# '*.exe' liefern nur String-Auflistungen zurück deren Weiterverarbeitung umständlich ist.
 +
 +# Am Unterschied zwischen ping.exe (DOS-Welt) und Test-NetConnection (PowerShell-Welt)
 +# den Mehrwert von Objekten schätzen lernen. Hier eine Prüfung ob ein Zeilhost erreichbar ist:
 +
 +  ping.exe  127.0.0.1 
 + (ping.exe  127.0.0.1)[8]
 + (ping.exe  127.0.0.1) | Select-String -Pattern 'Verloren = 0' 
 + (ping.exe  127.0.0.1) | Select-String -Pattern 'Verloren = 0' | Measure-Object 
 +((ping.exe  127.0.0.1) | Select-String -Pattern 'Verloren = 0' | Measure-Object).Count -eq 1
 +
 + Test-NetConnection -ComputerName 127.0.0.1
 +(Test-NetConnection -ComputerName 127.0.0.1).PingSucceeded
 +
 +#
 +# Rückgabe-Objekte analysieren mit Get-Member
 +#
 +
 +ping.exe 127.0.0.1 | Get-Member
 +Test-NetConnection -ComputerName 127.0.0.1 | Get-Member
 +
 +# Get-Member analysiert jedes Objete in der Pipeline und liefert gruppiert die Antwort auf:
 +# Vom welchen Typ ist das Objekt
 +# Welche Eigenschaften, Methoden und Ereignisse unterstützt das Objekt
 +
 +Get-ChildItem c:\ -Force | Get-Member # z.B. 2 unterschiedliche Objekt-Typen
 +Get-Process -Name notepad | Get-Member
 +
 +# Erklährung zum Ergebnis von Get-Member:
 +#
 +# TypeName <= Vollständige Typ-Name, z.B.: System .IO .FileInfo
 +# NAMESPACE.NAMESPACE.TYPENAME
 +# siehe google, bing, etc.
 +# Name <= Name des Member's
 +# MemberType <= Art des Members
 +# *Property TypeName PropertyName {get; set;}
 +# Methode void => Ohne Rückgabe
 +# ... MethodName(EingabeTyp'en)
 +# Definition <= Beschreibung
 +
 +Set-Content -Path c:\temp\test.txt -Value "Hallo Köln"
 +$datei = Get-ChildItem -Path C:\temp\test.txt
 +$datei.Length # long Length {get;}
 +$datei.Length = 99
 +$datei.CreationTime 
 +$datei.CreationTime = "2000-12-31" # datetime CreationTime {get;set;}
 +$datei | Get-Member
 +
 +$datei | Add-Member -MemberType NoteProperty -Name Bemerkung -Value "Eine Eigenschaft die diesem O. hinzugefügt wurde"
 +$datei | Get-Member
 +$datei.Bemerkung    # Mehrwert hinzufügen
 +$datei.VersionInfo  # Mehrwert hinzufügen
 +# z.B. Enthält Length die größe der Datei? Bzw. wenn ja, was KB, MB, ...?
 +# Evtl. im Internet nachlesen über den Typenname!
 +
 +$datei.Delete() # void Delete()
 +(Get-ChildItem -Path C:\temp -File).Delete()
 +
 +Get-Process -Name notepad | Get-Member
 +(Get-Process -Name notepad).WaitForExit(5000) 
 +(Get-Process -Name notepad).WaitForExit() 
 +
 +# ÜBUNG Köln in "Hallo Köln!" durch Würzburg ersetzen
 +# ÜBUNG Alle Dateien finden die älter sind als 180 Tage (OPTIONAL vom 1.1.2015 gerechnet)
 +
 +#endregion
 +#region Formatierung & Ausgabe (Format-*, ConvertTo-* Write-*, Out-*)
 +
 +#
 +# Übersicht
 +#
 +
 +Get-Command -Verb Format, ConvertTo, Out, Write -Module Microsoft.PowerShell.*
 +
 +#region Format-*
 +
 +#
 +# Format-Table
 +#
 +
 +Get-Process                                   # Autom. mit Default-Einstellung an ft
 +Get-Process | Format-Table *                  # Alle Spalten
 +Get-Process | ft Name, Company, CPU           # Spezielle Spalten
 +Get-Process | ft Name, Company, CPU -AutoSize # Performance !?
 +Get-Process | Format-Table Name, WorkingSet64
 +Get-Process | ft Name, @{Label="WS (MB)"; Expression={[Math]::Round($_.WorkingSet64 / 1MB, 2)}}
 +Get-Process | ft Name, @{Name="RAM (MB)"; Expression={"{0,6:0.00}" -F ($_.WorkingSet / 1MB)}} # Siehe F-Operator
 +
 +Get-EventLog -LogName System -Newest 10 
 +Get-EventLog -LogName System -Newest 10 | ft -Wrap              # Lange Text in Zeilen umbrechen
 +Get-EventLog -LogName System -Newest 10 | Get-Member | ft -Wrap
 +
 +Get-EventLog -LogName System -Newest 50 | ft -GroupBy EntryType
 +Get-EventLog -LogName System -Newest 50 | Sort-Object EntryType | ft -GroupBy EntryType
 +
 +Get-EventLog -LogName System -Newest 50 | Sort-Object EntryType | ft -GroupBy EntryType | Out-File -FilePath c:\temp\test.txt
 +
 +#
 +# Format-List
 +#
 +
 +Get-Process | fl Name, Company, CPU # Spezielle Spalten
 +Get-Process | fl *
 +Get-Process | Select-Object -First 1 | fl *
 +(Get-Process)[0] | fl *
 +
 +#
 +# Format-Wide
 +#
 +
 +Get-Alias | fw Name -Column 8
 +
 +# WICHTIG
 +# Die CmdLet's Format-* sind für die Endausgabe gedacht, diese Ergebnisse können
 +# nicht weiter verwertet werden !!!!!!!
 +# -------------------------------------
 +
 +#endregion
 +
 +#region Write-*
 +
 +"Hallo Köln!" # Default ist Write-Host
 +"Hallo Köln!" | Write-Host
 +"Hallo Köln!" | Write-Host -ForegroundColor Red # Nicht nutzen um Warnung und Fehler auszugeben
 +Write-Host "Standort: " -NoNewline; Write-Host "Köln"
 +"Hallo Köln!" | Write-Output # Das Ziel kann agegeben werden z.B. Datei, etc.
 +Write-Error "Achtung ich bin ein Fehler" # Stop das Script -ErrorAction ......
 +Write-Warning "und ich bin eine Warnung" # Evtl. wird das Script gestopt -WarningAction ..............
 +Write-Debug "und ich eine Debug-Meldung" # -Debug
 +
 +#
 +# Beispiel I:
 +#
 +
 +for ($i = 1 ; $i -lt 10 ; $i++) # $i++ => $i = $i + 1
 +
 +    [System.Threading.Thread]::Sleep(500) # Last-Simulator (Aufruf einer statischen Methode aus dem .NET Framework []::)
 +    Write-Host "." -ForegroundColor Yellow -NoNewline
 +}
 +
 +#
 +# Beispiel II:
 +#
 +
 +$guthaben = 12345.98
 +$konto = 47110815
 +Write-Host "Auf ihrem Konto `"$konto`" ist ein Guthaben von $guthaben!"
 +Write-Host 'Auf ihrem Konto $konto ist ein Guthaben von $guthaben!'
 +. "c:\$guthaben.txt"
 +
 +#endregion
 +
 +#region OUT-*
 +
 +Get-Command -Verb Out -Module Microsoft.PowerShell.*
 +
 +"Jetzt ist " + (Get-Date) | Out-File C:\temp\JetztIst.txt
 +Get-Content C:\temp\JetztIst.txt
 +
 +"Jetzt ist " + (Get-Date) | Out-File C:\temp\JetztIst.txt -Append
 +Get-Content C:\temp\JetztIst.txt
 +
 +Get-Process | ft * | Out-String -Width 500
 +
 +#endregion
 +
 +#region ConvertTo-
 +
 +Get-Command -Verb ConvertTo -Module Microsoft.PowerShell.*
 +
 +Get-Process | ConvertTo-Csv | out-File C:\temp\procs.csv
 +Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Csv -NoTypeInformation -Delimiter ";" | out-File C:\temp\procs.csv
 +. C:\temp\procs.csv
 +
 +Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Html | out-File C:\temp\procs.html
 +. C:\temp\procs.html
 +
 +#endregion
 +
 +# Übung: Eine Liste der laufenden Processes für
 +# Excel (csv) aufbereiten mit den Spalten
 +# Name, Arbeitsspeicher, Beschreibung
 +
 +# ÜBUNG: CSV-Datei aller laufenden Dienste für die spätere analyse! (import/export)
 +
 +#endregion
 +#region PowerShell-Provider und -Laufwerke als standadisierte Schnittstellen
 +
 +# Mit PowerShell-Provider werden div. 'DB' in/um Windows in der PS zur Verfügung gestellt, z.B.:
 +# - Dateisystem
 +# - Umgebungsvariablen
 +# - PowerShell-Variablen
 +# - PowerShell-Funktionen
 +# - Registrierungsdatenbank
 +# - X.509-Zertifikatsspeicher
 +# - [OPTIONAL] ActiveDirectory
 +# - [OPTIONAL] Exchange-Postfächer/-Ordner
 +# - [OPTIONAL] MS SQL-Server
 +# - u.v.m.
 +
 +Get-PSProvider # Durch das Laden eines Modules können weitere PS-Provider hinzukommen
 +Import-Module -Name ActiveDirectory
 +Get-PSProvider
 +
 +# Für die Interaktion mit den PS-Provider'n gibt es PSDrive's (Laufwerke)
 +
 +Get-PSDrive
 +
 +# Releative und absolute Pfade:
 +# . = Aktueller Ordner
 +# .. = Übergeordneter
 +# \ = Stamm
 +# ~ = Basis (Home-Verzeichnis)
 +
 +gci .
 +gci ..
 +gci \
 +gci ~
 +
 +gci c:\
 +gci variable:\
 +gci cert:\
 +gci env:\
 +gci hkcu:\
 +
 +
 +Set-Location C:\Windows\System32
 +Resolve-Path -Path .\msinfo32.exe
 +Test-Path    -Path C:\Windows\System32\msinfo32.exe
 +Test-Path    -Path variable:\a
 +Split-Path   -Path C:\Windows\System32\msinfo32.exe -Parent
 +Split-Path   -Path C:\Windows\System32\msinfo32.exe -Leaf
 +Join-Path    -Path C:\Windows\System32\ -ChildPath \msinfo32.exe
 +
 +# Kennt man einen kennt man alle :-)
 +# Befehle in einem PSProvider sind zu 100% übertragbar auf andere PSProvider
 +
 +Get-Command rm 
 +Get-Command del
 +Get-Alias -Definition Remove-Item
 +ri C:\Temp\test.htm -Force -WhatIf
 +ri HKCU:\SOFTWARE\7-Zip\FM\Columns -Force -WhatIf
 +
 +#
 +# ÜBERSICHT
 +#
 +
 +Get-Command -Name *item*, Set-Location -Module Microsoft.PowerShell.Management | Out-GridView
 +
 +Set-Location c:
 +Get-ChildItem
 +cd C:\Windows    # Alias für?
 +
 +Set-Location env:
 +Get-ChildItem
 +
 +Set-Location hkcu:
 +Set-Location Software
 +Set-Location ..
 +
 +# Exemplarisches Handling für den PSProvider "Registry"
 +
 +# Schlüssel 'Abc' erstellen
 +New-Item -Path HKCU:\Software\Abc -Force -ItemType key 
 +New-Item -Path c:\temp\abc.txt    -Force -ItemType file
 +New-Item -Path c:\temp\abc.txt    -Force -ItemType directory
 +
 +# Default-Wert für 'Abc' erstellen
 +New-Item -Path HKCU:\Software\Abc -Value "Def von Abc" -ItemType String -Force          
 +
 +# Eine NEUE Eigenschaft für den Schlüssel "Abc" erstellen
 +New-ItemProperty -Path HKCU:\Software\Abc -Name "Heute"  -Value '2016-01-19' -PropertyType 'String' -Force
 +New-ItemProperty -Path HKCU:\Software\Abc -Name "Anzahl" -Value 112          -PropertyType 'DWord'  -Force | Out-Null
 +
 +# Eine VORHANDENE Eigenschaft geändert
 +Set-ItemProperty -Path HKCU:\Software\Abc -Name "Anzahl" -Value 113 -Type QWord 
 +
 +# Den Schlüssel 'Abc' lesen
 +Get-Item -Path HKCU:\Software\Abc
 +
 +# BTW: Achtung
 +Get-Item      -Path C:\Windows
 +Get-ChildItem -Path C:\Windows
 +Get-ChildItem -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\  | ? Name -Like 'Run*'
 +Get-ChildItem -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\ | fl *
 +Get-ChildItem -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\  | ? PSChildName -Like 'Run*'
 +
 +# Den Wert von Heute des Schlüssels 'Abc' lesen:
 +Get-ItemProperty -Path HKCU:\Software\Abc -Name "Heute"
 +$heute = Get-ItemProperty -Path HKCU:\Software\Abc -Name "Heute"
 +$heute.Heute
 +(Get-ItemProperty -Path HKCU:\Software\Abc -Name "Heute").Heute
 +(Get-ItemProperty -Path HKCU:\Software\Abc).Heute
 +[datetime](gp HKCU:\Software\Abc).Heute
 +
 +# Den Default-Wert des Schlüssels 'Abc' lesen:
 +(Get-ItemProperty -Path HKCU:\Software\Abc).'(default)'
 +
 +# Die Eigenschaft 'Heute' löschen:
 +Remove-ItemProperty -Path HKCU:\Software\Abc -Name 'Heute'
 +
 +# Den Default-Wert des Schlüssels 'Abc' leeren:
 +Set-ItemProperty -Path HKCU:\Software\Abc -Name '(default)' -Value $null # Oder löschen und neu erstellen
 +
 +# Den Schlüssel 'Abc' mit allen Eigenschaften und Unterschlüssel löschen
 +Remove-Item -Path HKCU:\Software\Abc -Force
 +
 +#
 +# Zum Beispiel PSProvider "Function"
 +#
 +
 +Get-ChildItem function: | Out-GridView
 +Get-ChildItem Function:\C: | Select-Object -Property Name, Definition
 +function hkcu: { Set-Location hkcu: }
 +hkcu:
 +Get-ChildItem Function:\hkcu: | Format-List *
 +
 +#
 +# Weitere Beispiele:
 +#
 +New-Item -Path C:\temp\Scripts -ItemType directory -Force
 +New-PSDrive -Name Skripts -PSProvider FileSystem -Root C:\temp\Scripts
 +Set-Location skripts:
 +New-Item -Path .\test.txt -ItemType 'file' 
 +Get-PSDrive
 +
 +Get-ChildItem -Path HKLM:, HKCU: -Include '*PowerShell*' -Recurse -ErrorAction SilentlyContinue
 +
 +Get-ChildItem $HOME -Force
 +
 +#endregion
 +#region PowerShell und Remoting
 +
 +# WICHTIG: Das Netzwerk muss sicg im Domänen- bzw. Privaten-Netzwerkprofil befinden
 +Set-NetConnectionProfile -NetworkCategory Private
 +Get-NetConnectionProfile
 +
 +# Version 1: RPC (ab PowerShell 2.0)
 +# + Das geht immer, muss nicht aktiviert oder eingerichtet werden
 +# + Seit PowerShell 2.0
 +# - Nur möglich mit ausgewählten CmdLets
 +
 +Get-NetFirewallProfile
 +Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
 +
 +Get-Help * -Parameter "ComputerName" | Out-GridView # Auf 2.0 ausführen!!!
 +Get-Service -ComputerName 192.168.50.41, 192.168.50.50 | ft Name, Status, MachineName
 +
 +# Version 2: WinRM (ab PowerShell 3.0)
 +# - Muss aktiviert und eingerichtet (Client / Server)
 +# + Geht mit allen CmdLets/Scripten !!!
 +# Wichtig: Firewall TCP 5985
 +
 +# Einmal WinRM aktivieren (Für Quell- und Zielsystem auch gerne über eine GPO)
 +Enable-PSRemoting -Force
 +
 +# Einmal eine Client- und Zielsystem-Vertrauensstellung einrichten
 +$value = (gci -Path WSMan:\localhost\Client\TrustedHosts).Value
 +Set-Item -Path wsman:\localhost\client\trustedhosts -Value ($value + ", 192.168.50.41, 192.168.50.50")
 +# Möglich Werte sind auch: SqlServer01, ADS01.abc.local, *, 192.168.50.0
 +Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value "*"
 +gci -Path wsman:\localhost\client\trustedhosts
 +
 +#
 +# Nutzen
 +#
 +
 +# OPTIONAL wenn anderen Credential benötigt werden als der aktuelle Context
 +$cred = Get-Credential -Message "Bitte anmelden für ..." -UserName "Administrator"
 +
 +# EINE Session zum Zielsystem öffnen
 +$session1 = New-PSSession -ComputerName 192.168.50.50 -Credential $cred
 +
 +# Informationen über aktuelle Sessions
 +Get-PSSession
 +
 +# Eine offene Session nutzen
 +Enter-PSSession -Session $session1
 +Enter-PSSession -Id 1
 +Enter-PSSession -ComputerName 192.168.50.50 -Credential (Get-Credential)
 +
 +# TEST
 +New-Item c:\temp\Ak.txt -Value "Erzeug vom PC.. für PC.." -ItemType file
 +
 +# Zurück zur eigenen lokalen Session
 +Exit-PSSession
 +Get-PSSession # Session bleiben offen und aktiv
 +
 +# Aktive Sessions löschen
 +Remove-PSSession -Session $session1
 +Remove-PSSession -Id 1
 +Get-PSSession
 +
 +# -------------------------------------------------------------------------
 +
 +#
 +# Ein Script/CmdLet auf VIELE Zielssysteme ausführen
 +#
 +
 +# Mehrere Session erstellen
 +$sessions = New-PSSession -ComputerName 192.168.50.41, 192.168.50.50 -Credential $cred # Session-Array
 +$Session1 = New-PSSession -ComputerName 192.168.50.41 -Credential $cred                # Single Session
 +$Session2 = New-PSSession -ComputerName 192.168.50.41 -Credential $cred                # Single Session
 +
 +# Offene Sessions nutzen
 +Invoke-Command -Session $sessions            -ScriptBlock {Get-Process}
 +Invoke-Command -Session $session1, $session1 -ScriptBlock {Get-Process}
 +
 +# Sessions beenden
 +Remove-PSSession -Session $sessions
 +
 +# -------------------------------------------------------------------------
 +
 +# Was passiert beim starten einer UI-App?
 +Invoke-Command -Session $sessions -ScriptBlock {Start-Process calc.exe}
 +
 +# Siehe auch
 +Get-Command -Noun PSSession | Out-GridView
 +Get-Help about_*remote_* | Out-GridView
 +
 +# -------------------------------------------------------------------------
 +
 +# -> 6. PSRemoting deaktivieren
 +
 +Set-Item wsman:\localhost\client\trustedhosts "" -Force
 +Disable-PSRemoting -Force
 +Set-NetConnectionProfile -NetworkCategory Public
 +
 +#endregion
 +#region PowerShell-Funktionen erweitern (Module)
 +
 +# -> 1. Welche Module sind z.Zt. installiert?
 +
 +Get-Module -ListAvailable # Übersicht installierte Module, gruppiert nach Modul-Ordner
 +
 +<#
 +    Module die PC-Weit gelten:
 +        C:\Windows\system32\WindowsPowerShell\v1.0\Modules
 +     
 +    Module die nur für den entsprechenden User gelten:
 +        C:\Users\a.krick\Documents\WindowsPowerShell\Modules
 + 
 +    Expliziet eingebundene Module-Ordner, siehe:
 +#>
 +$env:PSModulePath -split ";"
 +
 +# -> 2. Benötigte Module installieren:
 +
 +<#
 +    Zum Beispiele:
 +        Microsoft
 +            Exchange Server
 +            Windows Server (WS2K8=Features, W7=RSAT installieren anschl. Programme u. Funktionen)
 +            SQL Server
 +            SharePoint
 +            System Center Operations Manager
 +            System Center Virtual Machine Manager
 +            System Center Data Protection Manager
 +            Windows Compute Cluster Server
 +            PowerTools for Open XML (Office2007+)
 +            Internet Information Services
 +            Windows 7 Troubleshooting Center
 +            Deployment Toolkit LOGINventory
 +        IBM
 +            Transporter Suite for Lotus Domino
 +            WebSphere MQ
 +        VMware
 +            Infrastructure Toolkit
 +        Quest
 +            Management Shell for Active Directory
 +        DIVERS
 +            Special Operations Software Specops Command
 +        U.V.M.
 +#>
 +Start-Process -FilePath "http://www.powershellgallery.com" # Siehe auch
 +
 +
 +#
 +# 1. SCHRITT: Modul installieren
 +# Durch eine setup.exe, *.msi oder über Eweitertete Installation eines Server-Produkts
 +#
 +
 +#
 +# z.B.: PowerShell Community Extensions (PSCX auf http://pscx.codeplex.com)
 +#
 +
 +. "C:\temp\Pscx-3.2.0 für PS40 Module.msi"
 +gci -Path "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3"
 +$env:PSModulePath -split ";" # Fehlt ein NEUER Eintrag PowerShell, ISE oder sogar PC neustarten
 +Get-Module -Name PSCX -ListAvailable # Installiert?
 +
 +#
 +# z.B.: Attila Krick PowerShell Tools (AKPT)
 +# manuell in einen der Modul-ORdner kopieren
 +#
 +
 +Copy-Item -Path C:\temp\AKPT -Destination C:\Users\akric\Documents\WindowsPowerShell\Modules\AKPT\ -Force -Recurse
 +Get-Module -Name AKPT -ListAvailable # Installiert?
 +
 +Find-Module    -Name AKPT # AB PS5.0 direkt aus dem Intenet- oder Firmenspeicher installieren
 +Install-Module -Name AKPT # AB PS5.0 direkt aus dem Intenet- oder Firmenspeicher installieren
 +
 +#
 +# 2. SCHRITT: Modul nutzbar machen
 +#
 +
 +Get-Module               # Übersicht der GELADENEN Modul (Nutzbar)
 +Import-Module -Name AKPT # Bei starten einer PS-Sitzung erneut ausführen
 +Get-Module
 +Remove-Module -Name AKPT # Entläd ein Modul (Nicht mehr nutzbar)
 +Get-Module
 +
 +# Seit der PowerShell 3.0 wird Import-Module automatisch ausgeführt
 +Get-HotKeys
 +Get-Module
 +
 +Get-Command -Module AKPT
 +Read-Window -Title "TITEL" -Message "NACHRICHT"
 +
 +Get-Command -Module PSCX | Out-GridView
 +gci function:\Out-Speech | select -ExpandProperty Definition
 +
 +#endregion
 +#region Variablen (*-Variable)
 +
 +#
 +# Übersicht
 +#
 +
 +Get-Help about_Variables            -ShowWindow # Grundlagen
 +Get-Help about_Scopes               -ShowWindow # Geltungsbereich
 +Get-Help about_Automatic_Variables  -ShowWindow # Systemvariable
 +Get-Help about_Hash_Tables          -ShowWindow # Hashtable
 +
 +Get-Help about_Objects              -ShowWindow # Objekt-Handling Teil 1
 +Get-Help about_Object_Creation      -ShowWindow # Objekt-Handling Teil 2
 +
 +Get-Help about_Special_Characters   -ShowWindow # ` Backtick (SHIFT + Akzent) Sonderzeichen wie z.B. Tab, NewLine, etc.
 +
 +# CmdLets um das Thema Variable
 +Get-Command -Name *variable* -Module Microsoft.PowerShell.Utility | Out-GridView
 +
 +# PowerShell-Provider für Variable
 +Get-ChildItem -Path variable: -Force | Out-GridView
 +
 +# Der Inhalt einer V. bleibt über die komplette Sitzung erhalten! (Nicht nur zur Laufzeit eines Skriptes)
 +
 +$einfach = 12               # In Variablen können einfache atomare Objekte gespeichert werden...
 +$komplex = (Get-Service)[0] # ... aber auch KOMPLEXE Objekte.
 + 
 +#
 +# Zuweisungen
 +#
 +
 +$PI = 99.9 # Variablen werden autom. mit der ersten Benutzung deklariert...
 +New-Variable -Name PI -Value 3.14 -Description "Kreiszahl Pi" -Option ReadOnly -Force # ... ODER per CmdLet.
 +
 +$var1   = "Hallo Köln"                                                        # Atomar String
 +$var2   = Get-Process                                                         # ALLE komplexen Process-Objekte
 +$var3   = (Get-Process)[0]                                                    # EIN komplex Process-Objekt
 +$var4   = $var2[0]                                                            # ---------- " -------------
 +$var5   = {Get-Process}                                                       # Script-Block
 +$array1 = 1, 2, 3, 4                                                          # eindimenmsionals Array
 +$array2 = (11,12,13), (21,22,23), (31,32,33)                                  # zweidimenmsionals Array (usw.)
 +$array3 = "Hallo", 12, (Get-Process)                                          # Gemischtes Array
 +$pc1    = [ordered]@{"Hostname"="PC1"; "IPv4"="192.168.50.51"; "Leasing"=123.68} # Hashtable
 +$array4 = "PC1", "192.168.50.51", 123.68                                      # Array
 +$env:windir                                                                   # Windows Umgebungsvariablen
 +$Function:prompt                                                              #
 +${c:\temp\JetztIst.txt} = Get-Date                                            # Datei asl Variable
 +
 +#
 +# Lesen / Nutzen
 +#
 +
 +$var1
 +Write-Host -Object $var1
 +$var5                    # Anzeige Inhalt Script-Block
 +. $var5                  # Script-Block ausführen (BTW: Datei werden so auch ausgeführt)
 +$array2[0]
 +$array2[1][1]
 +$array3 | select -Last 1
 +$array3.Length
 +$pc1["Hostname"]
 +$pc1["IPv4"]
 +$pc1["Leasing"]
 +
 +#
 +# Variablen auch gerne als Temp-Note nutzen, z.B.:
 +#
 +
 +$var4 = gci c:\ -File -Recurse -ErrorAction SilentlyContinue 
 +$var4.Length
 +$var4
 +$var5 = $var4 | ? Name -Like "*Microsoft*"
 +$var5.Length
 +$var5
 +
 +#
 +# Prüfen ob eine Variable (PSProvider) vorhanden ist
 +#
 +
 +Test-Path -Path variable:var1
 +Test-Path -Path variable:varX
 +
 +#
 +# Eine Variable muss immer von einem Typ sein
 +#
 +
 +$text    = "Hallo Köln!"    # System.String
 +$zahl1   = 123              # System.Int32
 +$zahl2   = 123.5            # System.Double
 +$istWahr = $true            # System.Boolean
 +$objekt1 = (Get-Process)[0] # System.Diagnostics.Process
 +$objekt2 = Get-Process      # System.Object[]
 +
 +# Typ der Variablen ermitteln
 +$objekt1.GetType() | ft Name, FullName, BaseType
 +$objekt2.GetType() | ft Name, FullName, BaseType
 +$objekt1 | Get-Member
 +$objekt2 | Get-Member # ACHTUNG: richtet sich immer an Array-Elemente
 +
 +$IchBinEineDateiOderOrdner -is [System.IO.FileInfo]
 +$zahl1 -is [System.Int64]
 +
 +
 +# TIP: i.d.R. den Typen fest angeben
 +[Int32]$alter = 12
 +$alter = "Hallo Köln!"
 +
 +# z.B.
 +
 +[datetime]$eingabe = Read-Host -Prompt "Alte Dateien finden vor welchen Datum (yyyy-MM-dd z.B. 2015-12-31)?"
 +gci C:\ -Recurse | ? LastAccessTime -le $eingabe
 +
 +$deDE = New-Object -TypeName System.Globalization.CultureInfo -ArgumentList "de-DE"
 +$eingabe2 = Read-Host -Prompt "Alte Dateien finden vor welchen Datum?"
 +gci C:\ -Recurse | ? LastAccessTime -le $eingabe2.ToDateTime($deDE)
 +
 +#
 +# Variablen evtl. aufräumen
 +#
 +
 +Remove-Variable -Name alter
 +Remove-Variable -Name PI -Force
 +Remove-Variable -Name * -Force -ErrorAction SilentlyContinue
 +
 +#
 +# Beispiele mit Variablen
 +#
 +
 +$geld = 123456.78
 +$geldSchön = $geld.ToString("#,##0.00 €") # Zahlen- / Datumstypen nach String formatieren
 +"Ihr Guthaben ist: $geldSchön"            # Inhalt einer V. in einen String betten
 +
 +$heute = Get-Date -DisplayHint Date
 +$heute.ToString("yyyy-MM-dd HH:mm:ss.fffffff")
 +$heute = Get-Date
 +$heute.ToString("yyyy yy MMMM MMM MM M dddd ddd dd d HH hh mm ss fffffff")
 +
 +# Datum des letzten Tages im akt. Monat?
 +$letzterTagImMonat = Get-Date -Year (Get-Date).Year -Month (Get-Date).Month -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0
 +$letzterTagImMonat = $letzterTagImMonat.AddMonths(1).AddDays(-1)
 +$letzterTagImMonat
 +
 +# Anzahl Tage bis Neujahr
 +[datetime]$neuJahr = "2017-01-01"
 +$dauer = $neuJahr - (Get-Date) # [System.DateTime] MINUS [System.DateTime] GLEICH [System.TimeSpan]
 +$dauer | Get-Member
 +$dauer | fl *
 +$dauer.Days
 +
 +#
 +# Gültigkeitsbereichen
 +#
 +
 +$wert = 23
 +function TestBereich
 +{
 +    "Wert in der Funktion: $wert"
 +    $wert = 43
 +    "Neuer Wert in der Funktion: $wert"
 +}
 +
 +TestBereich
 +"Wert außerhalb der Funktion: $wert"
 +
 +. TestBereich # . Abschirmung deaktiviert ehr NOGO!
 +"Wert außerhalb der Funktion: $wert"
 +
 +$global:wert2  = 123  # globale Variable NOGO
 +$private:wert3 = 321  # private Variable
 +$script:wert3  = 321  # skriptglobale Variable NOGO
 +
 +#endregion
 +#region Operatoren
 +
 +#
 +# ÜBERSICHT
 +#
 +
 +Get-Help about_Operators            -ShowWindow # Übersicht aller Operatoren
 +Get-Help about_Operator_Precedence  -ShowWindow # Vorrangregeln
 +Get-Help about_Arithmetic_Operators -ShowWindow # Arithmetische Operatoren
 +Get-Help about_Assignment_Operators -ShowWindow # Zuweisungs- Operatoren
 +Get-Help about_Logical_Operators    -ShowWindow # Logische Operatoren
 +Get-Help about_Comparison_Operators -ShowWindow # Vergleichs- Operatoren
 +Get-Help about_Type_Operators       -ShowWindow # Objekt- Operatoren
 +Get-Help about_Split                -ShowWindow # Objekt- Operatoren
 +Get-Help about_Join                 -ShowWindow # Objekt- Operatoren
 +
 +#region about_Assignment_Operators
 +
 +$zahl=1  # Start: 1
 +$zahl++  # $zahl = $zahl + 1
 +$zahl--  # $zahl = $zahl - 1
 +$zahl+=2 # $zahl = $zahl + 2
 +$zahl-=3 # $zahl = $zahl - 3
 +$zahl++  # $zahl = $zahl + 1
 +$zahl    # Ergebnis: 1
 +
 +$zahl=-10 # Ergebnis: ???
 +$zahl
 +
 +#endregion
 +
 +#region about_Arithmetic_Operators
 +
 +# Siehe About-Seite!
 +
 +#endregion
 +
 +#region about_Logical_Operators
 +
 +$true -and $true
 +$true -or  $false
 +($true -and $true) -or ($true -or  $false)
 +!$true -and $true
 +-not $true -and $true
 +
 +#endregion
 +
 +#region about_Comparison_Operators
 +
 +"Köln" -eq "köln"  # eq = ieq
 +"Köln" -ceq "köln"
 +"Köln" -ieq "köln"
 +"Köln" -cin "Köln", "Hamburg", "München"
 +"Köln", "Hamburg", "München" -contains "Köln"
 +
 +# ACHTUNG
 +       "010" -eq  010   # False
 +        010  -eq "010"  # True
 +[Int32]"010" -eq  010   # True
 +
 +#
 +# -like
 +#
 +
 +"abc" -like "?b?" # *, ?, ...
 +
 +# -match (Reguläre Ausdrücke, kurz "regex", Wikipedia)
 +
 +<#
 +    REGEX = TARGET
 +    a = a
 +    [abc] = a b c
 +    a{2,5} = aa aaa aaaa aaaaa
 +    . = Alle Zeichen außer Zeilenumbrüche
 +    \ = Maskieren
 +    ^ = Vom ersten Zeichen
 +    $ = Bis zum lezten Zeichen
 +    + = {1,}
 +    * = {0,}
 + 
 +#>
 +
 +"AB678.docx" -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # True
 +"AB678.doc"  -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # True
 +"XX678.doc"  -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # True
 +"XX123.docx" -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # True
 +"X123.docx"  -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # False
 +"AA1.docx"   -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # False
 +"AA123.xlsx" -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" # False
 +
 +"a.krick@gfu.net" -match "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
 +
 +#
 +# Array-Vergleich
 +#
 +
 +10,11,12,13,14,10 -eq 10
 +10,11,12,13,14,10 -ne 10
 +10,11,12,13,14,10 -gt 10
 +
 +#
 +# Vergleichsoperatoren z.B. nutzen in: Where-Object, if(){}
 +#
 +if(10 -gt 5)
 +{
 +    # CmdLet
 +}
 +else # Optional
 +{
 +    # CmdLet
 +}
 +
 +10,11,12,13 -contains 10
 +(10,11,12,13), (14,15,16,17) | Where-Object   -FilterScript {$_ -contains 10}
 +(10,11,12,13), (14,15,16,17) | ForEach-Object -Process      {$_ -contains 10}
 +
 +#endregion
 +
 +#region about_Type_Operators
 +      
 +(Get-Process)[0] -is [System.Diagnostics.Process] # Ist ein Objekt von einem bestimmten Typen
 +(Get-Process)    -is [System.Array]
 +
 +          "2015-03-18" -is [DateTime]
 +[DateTime]"2015-03-18" -is [DateTime]
 +
 +#endregion
 +
 +#region -f (Format-Operator) s. about_operators
 +
 +# "FORMAT" -f Value[s]
 +
 +"Wert 1 ist {0}, der letzte Wert ist {3} und die anderen sind: {1}, {2}" -f 10,11,99,230,1024 
 +"Zahlenformat: {0}"          -f 123456.6789
 +"Zahlenformat: {0:#,##}"     -f 123456.6789
 +"Zahlenformat: {0:0.0}"      -f 123456.6789
 +"Zahlenformat: {0:#,##0.00}" -f 123456.6789
 +
 +"Heute ist {0}"                                                                       -f (Get-Date)
 +"Heute ist {0} und wir haben {1} Uhr"                                                 -f (Get-Date), (Get-Date)
 +"Heute ist {0} und wir haben {0} Uhr"                                                 -f (Get-Date)
 +"Heute ist {0:dd.MM.yyyy} und wir haben {0:HH:mm} Uhr"                                -f (Get-Date)
 +"Heute ist {0:dddd.} der {0:d. MMMM} anno {0:yyyy HH:mm} und {0:ss.fffffff} Sekunden" -f (Get-Date)
 +
 +"{0,-10:0.0} | {1,-10:0.0} | {2,-10:0.0}" -f   "Wert A", "Wert B", "Sum"
 +"{0, 10:0.0} | {1, 10:0.0} | {2, 10:0.0}" -f   12,  23, 167.889
 +"{0, 10:0.0} | {1, 10:0.0} | {2, 10:0.0}" -f  212, 123,  67.89
 +"{0, 10:0.0} | {1, 10:0.0} | {2, 10:0.0}" -f 3312,   3,   7.8
 +
 +Write-Host "{0, 10:0.0} | {1, 10:0.0} | {2, 10:0.0}" -f 3312,   3,   7.8
 +
 +#
 +# Beispiele
 +#
 +
 +$pcs = (1..99 | ForEach-Object -Process {"PC{0:000}" -f $_})
 +$session = New-PSSession -ComputerName $pcs
 +Invoke-Command -Session $session -ScriptBlock {Restart-Computer}
 +
 +"Log_{0:yyyyMMddHH}.log" -f (Get-date)
 +
 +#endregion
 +
 +#region about_Split
 +
 +$result = "ADS001;127.0.0.1;1234.67;" -split ";"
 +$result[1]
 +
 +#endregion
 +
 +#region about_join
 +
 +$result = "Hallo Köln!", "127.0.0.1", 123.45
 +$result -join ";"
 +
 +#endregion
 +
 +#region replace
 +
 +"Hallo Köln!" -replace "Köln", "Berlin"
 +
 +#endregion
 +
 +#endregion
 +#region WMI
 +
 +Get-WmiObject -List Win32_
 +
 +$FormatEnumerationLimit = -1
 +Get-WmiObject -List Win32_Product | fl Methods, Properties, Events
 +
 +Get-WmiObject -Class Win32_Product # Software-Inventarisierung
 +Get-WmiObject -Class Win32_Product | select -First 1 | fl *
 +Get-WmiObject -List Win32_printer  # Drucker
 +
 +
 +#region Ein MSI-Paket installieren
 +
 +$msi = "c:\7z920-x64.msi"
 +$produkte = Get-WmiObject -Class Win32_Product -List
 +"Installation läuft ...."
 +$ergebnis = $produkte.Install($msi)
 +"... Installation fertig mit dem Ergebnis {0}" -f $ergebnis.ReturnValue
 +Get-WmiObject -Class Win32_Product | where Name -like "*7-zip*" | Get-Member
 +# ReturnValue siehe http://msdn.microsoft.com/en-us/library/aa390890(v=vs.85).aspx
 +
 +#endregion
 +
 +#
 +# Invoke-CimMethod
 +#
 +$inst = Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE name like 'notepad%'"
 +Invoke-CimMethod -InputObject $inst -MethodName GetOwner
 +
 +#
 +# s. Register-WmiEvent
 +#
 +
 +#endregion
 +#region COM-Objekte
 +
 +#
 +# BEISPIEL Excel
 +#
 +
 +# (Excel bzw. Office muss installiert sein, Office PIA 2010 downloaden)
 +# BESSER: CSV, 'OpenXML for Office'
 +
 +$xls = New-Object -ComObject "Excel.Application"
 +$xls.Visible = $true # Debugging
 +$wb = $xls.Workbooks.Add()
 +$ws = $wb.Worksheets.Add()
 +$a1 = $ws.Range("A1")
 +$a1.Value2 = "PowerShell ist spitze!"
 +$wb.SaveAs("c:\temp\meinmappe.xlsx")
 +$xls.Quit()
 +
 +#
 +# BEISPIEL Word
 +#
 +
 +$word=New-Object -ComObject "Word.Application" 
 +$word.Visible=$true
 +
 +$doc=$word.Documents.Add()
 +
 +$win=$doc.Windows.Item(1)
 +$win.View.ShowAll=$true
 +
 +$objAbs=$doc.Paragraphs.Item(1)
 +$objAbs.Range.Text="Titel`rAbsatztext"
 +$objAbs.Range.Text=($objAbs.Range.Text + "zweite Zeile")
 +$objAbs=$doc.Paragraphs.Item(1)
 +$objAbs.Style="Überschrift 1"
 +
 +$erg=$doc.PrintOut([ref]$false)     # VORTEIL von COM-Object
 +$erg=$doc.prin
 +
 +$doc.SaveAs([ref]"c:\Temp\PS und Word.docx")
 +$doc.Close([ref]$false)
 +$word.Quit()
 +$word=$null
 +
 +Get-WmiObject Win32_COMClass # Davon können COM-Objekte erzeugt werden
 +
 +#endregion
 +#region Registry
 +
 +#s. region 'PowerShell-Provider'
 +
 +#endregion
 +#region Verarbeiten von TXT
 +
 +#
 +# ÜBERSICHT
 +#
 +
 +Get-Command -Noun Content -Module Microsoft.PowerShell.*
 +
 +gci C:\Windows\Logs -Recurse -File -ea SilentlyContinue | 
 +    ? Extension -EQ ".log"
 +    Get-Content -ea SilentlyContinue | 
 +    Select-String -Pattern "Error"
 +    Add-Content -Path C:\Temp\PSTest\errors.txt
 +
 +Send-MailMessage `
 +    -To "a.krick@outlook.com" `
 +    -From "teilnehmer@gfu.net" `
 +    -Subject "Error" `
 +    -Body ([String]::Concat("C:\Temp\PSTest\errors.txt")) `
 +    -Attachments "C:\Temp\PSTest\errors.txt" `
 +    -SmtpServer 192.168.50.10 `
 +    -Port 25 `
 +    -ErrorAction Stop
 +
 +Remove-Item -Path c:\temp\errors.txt
 +
 +# Was machen Sie so mit *.txt-Dateien?
 +
 +#endregion
 +#region Verarbeiten von CSV
 +
 +# Schreiben
 +Get-Process | 
 +    Select-Object -Property Name, Company | 
 +    ConvertTo-Csv -NoTypeInformation -Delimiter ";" |
 +    Add-Content -Path c:\temp\procs.csv
 +
 +# Lesen
 +$result = Get-Content -Path c:\temp\procs.csv
 +foreach($item in $result) {
 +    "NAME {0} FIRMA {1}" -f ($item -split ";")
 +}
 +
 +#endregion
 +#region Verarbeiten von XML
 +
 +#
 +# Auswerten
 +#
 +
 +Invoke-WebRequest -Uri "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml" |
 +    Select-Object -ExpandProperty Content |
 +    Set-Content "C:\temp\eurofxref-daily.xml" -Force
 +
 +$xmlDokument = [xml](Get-Content -Path "C:\temp\eurofxref-daily.xml")
 +$xmlDokument.Envelope.Cube.Cube.Cube | ? currency -EQ "RUB" | Select-Object -ExpandProperty rate
 +$xmlDokument.Envelope.Cube.Cube.Cube | Out-GridView -OutputMode Single
 +
 +$xmlDokument                         | Get-Member -MemberType Properties
 +$xmlDokument.Envelope                | Get-Member -MemberType Properties
 +$xmlDokument.Envelope.Cube           | Get-Member -MemberType Properties
 +$xmlDokument.Envelope.Cube.Cube      | Get-Member -MemberType Properties
 +$xmlDokument.Envelope.Cube.Cube.Cube | Get-Member -MemberType Properties
 +$xmlDokument.Envelope.Cube.Cube.Cube.currency 
 +$xmlDokument.Envelope.Cube.Cube.Cube.rate
 +
 +#
 +# Schreiben
 +#
 +
 +# z.B. Konfigurationsdatei
 +
 +$config = [xml]@"
 +<?xml version="1.0" encoding="utf-8" ?>
 +<Einstellungen>
 +    <Server Name="ADC01" IP="127.0.0.1" />
 +    <Server Name="ADC02" IP="127.0.0.1" />
 +</Einstellungen>
 +"@
 +$config.Einstellungen.Server  | Select-Object -ExpandProperty Ip
 +$config.Einstellungen.Server  | ? Name -Like "*02"
 +($config.Einstellungen.Server | ? Name -Like "*02").IP = "192.168.50.32"
 +$config.Einstellungen.Server  | ? Name -Like "*02"
 +
 +$config.Save("C:\temp\einstellung.xml")
 +
 +$result = [xml](Get-Content "C:\temp\einstellung.xml")
 +$result.Einstellungen.Server
 +
 +#endregion
 +#region Verarbeiten von Ordner & Dateien
 +
 +# Siehe
 +
 +Move-Item
 +Copy-Item
 +Remove-Item
 +New-Item
 +Get-Item
 +
 +#
 +# Rechte
 +#
 +
 +Get-Acl -Path C:\temp | fl *
 +
 +$DogACL = Get-Acl C:\temp\einstellung.xml
 +$DogACL | Get-Member
 +$DogACL.SetAccessRuleProtection($true, $false) # isProtected, preserveInheritance Vererbung aktivieren
 +Set-Acl -Path C:\Cat.txt -AclObject $DogACL
 +
 +# Etwas auf allen Laufwerken suchen
 +
 +Get-PSDrive -PSProvider FileSystem |
 +    Select-Object -ExpandProperty Root |
 +    gci -Force -File -ea SilentlyContinue
 +
 +# Pfad und Besitzer anzeigen
 +
 +"C:\Windows" | gci -Force -File -ea SilentlyContinue | Select-Object -ExpandProperty FullName
 +"C:\Windows" | gci -Force -File -ea SilentlyContinue | Get-Acl | Select-Object -ExpandProperty Owner
 +
 +"C:\Windows" | gci -Force -File -ea SilentlyContinue | % -Process {
 +    $ht = @{"FullName"=$_.FullName;
 +            "Owner"   =($_ | Get-Acl | Select-Object -ExpandProperty Owner)}
 +    New-Object PSObject -Property $ht
 +} | Out-GridView
 +
 +# Oder so
 +
 +"C:\Windows" | gci -Force -File -ea SilentlyContinue | % -Process {
 +    $_ | Add-Member -MemberType NoteProperty -Name Owner `
 +                    -Value ($_ | Get-Acl | Select-Object -ExpandProperty Owner)
 +    $_
 +} | Select-Object FullName, Owner | Out-GridView
 +
 +# Belegter / Freier Speicher auf Festplatten
 +Get-PSDrive -PSProvider FileSystem
 +
 +# Doppelte Dateien finden
 +Get-FileHash C:\Windows\explorer.exe -Algorithm SHA512
 +
 +# Dateien- / Ordner überwachen
 +$fsw = New-Object System.IO.FileSystemWatcher
 +$fsw.Path = "c:\temp"
 +$fsw.Filter="*.txt"
 +$action = {
 +    Write-Warning ("Dateiänderung: {0} {1}" -f $eventArgs.FullPath, $eventArgs.ChangeType)
 +}
 +Register-ObjectEvent -InputObject $fsw -EventName Created -Action $action
 +Register-ObjectEvent -InputObject $fsw -EventName Deleted -Action $action
 +Get-EventSubscriber
 +Unregister-Event -SubscriptionId 3
 +Unregister-Event -SubscriptionId 4
 +
 +#region Netzwerk-Freigabe erstellen
 +
 +New-SmbShare -Name Data -Path C:\Windows
 +Remove-SmbShare -Name Data -Confirm:$false
 +
 +#endregion
 +
 +#endregion
 +
 +#endregion
 +#region AUFBAU (Scripting)
 +
 +#region > > > AGENDA < < <
 +
 +#PowerShell - Aufbau
 +
 +Start-Process "https://www.gfu.net/seminare-schulungen-kurse/erweiterungen_sk60/powershell-aufbaukurs_s1388.html"
 +
 +## FLUSSKONTROLLE IN SKRIPTEN
 +### Das if/elseif/else Statement [X]
 +### Das switch Statement [X]
 +### Die while Schleife [X]
 +### Die do ... while/until Schleife [X]
 +### Die for Schleife [X]
 +### foreach Schleife [X]
 +### break & continue Klausel [X]
 +
 +## BENUTZERDEFINIERTE FUNKTIONEN & SKRIPTE
 +### Grundlagen von Funktionen [X]
 +### Der Dot Operator [X]
 +### Formale Parameter und das param-Statement [X]
 +### Rückgabewerte von Funktionen [X]
 +### Function vs. Filter [X]
 +
 +## OBJEKTHANDLING
 +### SkriptBlock - Grundlagen [x]
 +### Erzeugen & Manipulieren von Objekten [x]
 +### Arbeiten am Typsystem [x]
 +### Typerweiterung [x]
 +
 +## FEHLER-MANAGEMENT
 +### Was-wäre-wenn Szenarien [x]
 +### Fehlertoleranz festlegen [x]
 +### Fehler erkennen und darauf reagieren [x]
 +### Error Records - Details zum Fehler [x]
 +### Exceptions verstehen [x]
 +### Code schrittweise ausführen: Haltepunkte [x]
 +
 +## SICHERHEIT
 +### Absichern der PowerShell-Umgebung [x]
 +### Signieren von Skripten [x]
 +
 +## .NET, C# UND VB.NET
 +### PowerShell & .NET-OOP Einblick [x]
 +### GUI mit .NET (WinForms, WPF) Einblick [X]
 +
 +#endregion
 +
 +#region Kontrollstrukturen (if, switch, while, do, for & foreach)
 +
 +# TIP: STRG + J (Snippets)
 +
 +#
 +# if
 +#
 +
 +get-help about_if -ShowWindow
 +
 +if (10 -lt 11)
 +{
 +    "10 ist kleiner 11"
 +}
 +
 +$x = 12
 +if ($x -lt 20)
 +{
 +    "$x ist kleiner 20"
 +}
 +else
 +{
 +    "$x ist größer 20"
 +}
 +
 +$x = 16
 +if ($x -lt 15)
 +{
 +    "$x ist kleiner 15"
 +}
 +elseif ($x -lt 20)
 +{
 +    "$x ist kleiner 20 und größer 15"
 +}
 +else
 +{
 +    "$x ist größer 20"
 +}
 +
 +#
 +# switch
 +#
 +
 +get-help about_switch -ShowWindow
 +
 +$x = "Value3"
 +switch -casesensitive ($x)
 +{
 +    'value1' # Prüfung gegen $x
 +    {
 +        # ... Code
 +        break # Option: Und Fertig
 +    }
 +    
 +    {$_ -in 'A','B','C' # Prüfung gegen $x
 +    {
 +    }
 +
 +    'value3' {}
 +    
 +    Default # Optional, Wenn keine Prüfung passt
 +    {
 +    }
 +}
 +
 +#
 +# do / while
 +#
 +
 +get-help about_do -ShowWindow
 +
 +do
 +{
 +    # Code
 +}
 +until ($x -gt 0) # Fußgesteuerte Schleife, Solange eine Be. nicht zutrifft
 +
 +do
 +{
 +    # Code
 +}
 +while ($x -gt 0) # Solange die Be. zutrifft
 +
 +while ($x -gt 0) # Kopfgesteuerte Schleife
 +{
 +    # Code
 +}
 +
 +#
 +# for
 +#
 +
 +get-help about_if -ShowWindow
 +
 +for ($i = 1 ; $i -lt 99 ; $i++) # $i = $i + 1
 +{
 +    # Code
 +    $i
 +    #break
 +}
 +
 +$prozesse = Get-Process
 +for ($i = 0; $i -lt $prozesse.length; $i++)
 +
 +    $prozesse[$i]
 +    if($prozesse[$i].Company -eq "Virus Inc.")
 +    {
 +        $prozesse[$i].Kill()
 +    }
 +}
 +Get-Process | ? Company -eq "Virus Inc." | Stop-Process
 +
 +#
 +# foreach
 +#
 +
 +get-help about_foreach -ShowWindow
 +
 +foreach ($item in $collection) { }
 +
 +foreach ($item in (Get-Process))
 +
 +    if($item.Company -eq "Virus Inc.")
 +    {
 +        $item.Kill()
 +    }
 +}
 +
 +#
 +# Alle Syntax-Blöcke können per CTRL + J als Vorlage erzeugt werden
 +#
 +
 +#endregion
 +#region Bentuzerdefnierte Funktionen (eigene CmdLet's)
 +
 +#
 +# CmdLets können auf zwei Arten definiert werden:
 +# 1. per C# oder VisualBasic.NET-Code über VisualStudio => MeineCmdLets.DLL
 +# 2. per PowerShell-Funktion mit dem Namen VERB-NOUN => MeineCmdLets.PS1
 +# WICHTIG: Diese CmdLets können erst genutzt werden, wenn sie im PSDrive function: etnhalten sind.
 +# => Durch AUSFÜHREN der Definition
 +#
 +
 +#
 +# ÜBERSICHT
 +#
 +
 +Get-Help about_Comment_Based_Help                -ShowWindow # Beschreibung der eigenen CmdLet-Hilfe für das PS-Hilfesystem.
 +Get-Help about_Functions                         -ShowWindow # Grundlagen zu benutzerdefinierten Funktion
 +Get-Help about_Functions_Advanced                -ShowWindow # Grundlagen zu benutzerdefinierten CmdLet
 +Get-Help about_Functions_Advanced_Methods        -ShowWindow 
 +Get-Help about_Functions_Advanced_Parameters     -ShowWindow # Beschreibung der eigenen CmdLet-Parameter u.a. Validierung
 +Get-Help about_Functions_CmdletBindingAttribute  -ShowWindow # Beschreibung der eigenen CmdLet-Parameter bzgl. Pipeline-Verarbeitung
 +Get-Help about_Functions_OutputTypeAttribute     -ShowWindow # Beschreibung von Rückgabe-Objekte wie PSCustomObject
 +
 +#
 +# Lernen von vorhanden CmdLets:
 +#
 +
 +Get-Command -CommandType Function | measure
 +gci Function:\New-Guid | Select-Object -ExpandProperty Definition
 +[GUID]::NewGuid() # Ah-ha, das führt das CmdLet New-Guid aus.
 +
 +# BTW ACHTUNG: Passwörter haben im Code nichts verloren!!!!!
 +
 +#region Einführungsbeispiel Get-About um die tägliche Arbeit mit der Hilfe zu erleichtern
 +
 +Get-Help about_*                                                            # Täglich neu suchen ist doof :-(
 +Get-Help about_* | Out-GridView                                             # Täglich viel tippen ist auch doof :-(
 +Get-Help about_remote_disconnnnected_Sessssions                             # Tippfehler ist erst recht doof :-(
 +Get-Help about_remote_disconnected_Sessions -ShowWindow                     # Schonwieder viel tippen :-(
 +Get-Help about_* | Out-GridView -OutputMode Multiple | Get-Help -ShowWindow # Schon besser, aber immer noch zuviel zu tippen
 +
 +#
 +# Lösung: Eigenes CmdLet
 +#
 +
 +function Get-About
 +{
 +    Get-Help about_* | Out-GridView -OutputMode Multiple | Get-Help -ShowWindow 
 +}
 +
 +Get-About
 +
 +#
 +# o.a. Lösung verfeinern:
 +#
 +
 +function Get-About
 +{
 +    param([string]$Filter)
 +
 +    $result = Get-Help -Name "about_*$Filter*"
 +    if($result -is [array])
 +    {
 +        $result | Out-GridView -OutputMode Multiple | Get-Help -ShowWindow 
 +    }
 +    else
 +    {
 +        Get-Help -Name "about_*$Filter*" -ShowWindow
 +    }
 +}
 +
 +Get-About -Filter if
 +
 +#endregion
 +
 +#region Unterschied Funktion vs. CmdLet-Funktion
 +
 +function Fläche([int]$Breite, [int]$Länge)
 +{
 +    $fläche = $Breite * $Länge
 +    return $fläche
 +}
 +
 +# Um eine Funktion benutzen zu können muss diese in den
 +# PowerShell-Provider function abgelegt werden
 +# durch das Ausführen der Definition
 +
 +gci Function:\Fläche
 +Fläche 10 10
 +
 +# wird die Session beendet wird die Funktion wieder gelöscht
 +
 +Remove-Item Function:\Fläche -Force
 +Fläche 10 10
 +
 +# Nachteile Funktion 'Fläche'
 +# Keine Parameter-Prüfung
 +# Keine Interaktion mit Get-Help
 +# Keine Interaktion Pipline-Verarbeitung
 +
 +# LÖSUNG: die Funktion als CmdLet ausbauen,
 +# möglich könnte sein:
 +
 +Get-Help Get-Fläche -ShowWindow
 +Get-Fläche -Breit 10 -Länge 15.5
 +Get-Fläche 10 15.5
 +Get-Fläche 10 15.5 | Out-GridView
 +"10x20", "55x99", "1.5x0.5" | Get-Fläche
 +Get-Fläche 10         # Fehler
 +Get-Fläche -10 15.5   # Fehler
 +Get-Fläche 101 101    # Fehler
 +Get-Fläche zehn zwölf # Fehler
 +
 +#
 +# Umsetzung:
 +# Wichtig: in einer eigenständigen *.ps1 entwickeln um mit F5 den Funktionsspeicher zu aktualisieren
 +#
 +
 +function Get-Fläche
 +{
 +    <#
 +        .Synopsis
 +            Flächenberechnung
 +         
 +        .DESCRIPTION
 +            Berechnet die Fläche aus Breit und Länge.
 +         
 +        .EXAMPLE
 +            Get-Fläche -Breit 10 -Länge 15.5
 +            Berechnet die Fläche aus Breit 10 mal Länge 15,5
 + 
 +        .EXAMPLE
 +            Get-Help Get-Fläche -Full
 + 
 +        .EXAMPLE
 +            Get-Fläche -Breit 10 -Länge 15.5
 + 
 +        .EXAMPLE
 +            Get-Help Get-Fläche -Full
 + 
 +        .EXAMPLE
 +            Get-Fläche 10 15.5 -Verbose
 + 
 +        .EXAMPLE
 +            Get-Fläche 10 15.5 | Out-GridView
 + 
 +        .EXAMPLE
 +            "10x20", "55x99", "1.5x0.5" | Get-Fläche
 + 
 +        .EXAMPLE
 +            "10x20", "55x99", "1.5x0.5" | Get-Fläche | Out-GridView
 + 
 +        .EXAMPLE
 +            "10x20", "55x99", "1.5x0.5" | Get-Fläche | Sort-Object Result -Descending
 +    #>
 +
 +    [CmdletBinding()]
 +    [Alias('gf')]
 +    [OutputType([PSObject])]
 +    Param
 +    (
 +        # Breite der Fläche
 +        [Parameter(ParameterSetName = 'BreiteLänge',
 +                   Mandatory        = $true,
 +                   Position         = 0)]
 +        [ValidateRange(0.1, 99)]
 +        [double]$Breite = 1,
 +
 +        # Länge der Fläche
 +        [Parameter(ParameterSetName = 'BreiteLänge',
 +                   Mandatory        = $true,
 +                   Position         = 1)]
 +        [ValidateRange(0.1, 99)]
 +        [double]$Länge = 1,
 +
 +        [Parameter(ParameterSetName  = 'Pipline',
 +                   Mandatory         = $true,
 +                   ValueFromPipeline = $true)]
 +        [ValidatePattern('^[0-9\.]{1,4}x[0-9\.]{1,4}$')]
 +        [string]$InputObject = [string]::Empty
 +    )
 +
 +    Begin
 +    {
 +        Write-Verbose -Message "Get-Fläche gestartet...."
 +    }
 +
 +    Process
 +    {
 +        if($PSCmdLet.ParameterSetName -eq 'Pipline')
 +        {
 +            $splits = $InputObject.Split('x')
 +            $Breite = [double]$splits[0]
 +            $Länge  = [double]$splits[1]
 +        }
 +
 +        $definition = '{0,5:0.00} x {1,5:0.00}' -F $Breite, $Länge
 +        $fläche = $Breite * $Länge
 +        $result = New-Object -TypeName PSObject -Property ([ordered]@{Definition=$definition; Result=$fläche})
 +        return $result
 +    }
 +
 +    End
 +    {
 +    }
 +}
 +
 +#endregion
 +
 +#
 +# Weitere Beispiele siehe 'TIPPS, TRICKS und BEISPIELE' am Ende dieser Datei
 +#
 +
 +#endregion
 +#region Benutzer-Interaktion (Einfach UI)
 +
 +#
 +# Read-Host
 +# - Ließt die Eingabe über die Tastatur bis zu einem 'Enter'
 +# - Rückgabe ist immer ein 'System.String'
 +#
 +
 +Read-Host -Prompt "Wieviel Geld wollen Sie abheben"
 +# i.d.R. in eine Variable speichern oder über die | weiter geben
 +$userInput = Read-Host -Prompt "Wieviel Geld wollen Sie abheben"
 +
 +#
 +# Einmal Perfekt:
 +#
 +
 +$deDE = New-Object -TypeName System.Globalization.CultureInfo -ArgumentList 'de-DE'
 +$userInput = Read-Host -Prompt "Wieviel Geld wollen Sie abheben"
 +[decimal]$betrag = $userInput.ToDecimal($deDE)
 +$betrag
 +$betrag.GetType()
 +[decimal]1000 - $betrag
 +
 +# z.B. auch bei einem Datum:
 +$userInput = Read-Host -Prompt "Dateien finden die älter sind als"
 +[datetime]$datum = $userInput.ToDateTime($deDE)
 +$datum
 +
 +#
 +# Nicht so gut ist:
 +#
 +
 +# Keinen Einfluss auf die Konvertierung, z.B. fürs Deutsche
 +[decimal]$eingabe = Read-Host -Prompt "Wieviel Geld wollen Sie abheben" 
 +
 +# Eine falsche Zuweisung ist später immer noch möglich
 +$eingabe = [decimal](Read-Host -Prompt "Wieviel Geld wollen Sie abheben")
 +$eingabe = Get-Process
 +
 +#
 +# Out-GridView
 +#
 +
 +$processes = Get-Process -IncludeUserName | ? UserName -Match 'akric'
 +$killMe = $processes | Out-GridView -Title 'Welche Prozesse sollten beendet werden' -OutputMode Multiple
 +$killMe | Stop-Process -Force -WhatIf
 +
 +# Als Einzeiler
 +Get-Process -IncludeUserName | 
 +    ? UserName -Match 'akric'
 +    Out-GridView -Title 'Welche Prozesse sollten beendet werden' -OutputMode Multiple | 
 +    Stop-Process -Force -WhatIf
 +#
 +# Weitere Beispiele für Out-GridView und Interaktion mit einem User
 +#
 +
 +$betrag = 10, 25, 50, 100, 250 | Out-GridView -OutputMode Single
 +
 +$antwort = "JA, Dateien archivieren", "NEIN, Dateien am Ort belassen" | Out-GridView -OutputMode Single
 +$antwort.StartsWith('NEIN')
 +
 +#
 +# 'richtige' MessageBox'en nur über das .NET Framework:
 +#
 +
 +[System.Windows.Forms.MessageBox]::Show('Hallo Köln')
 +
 +$title         = "Dateien archivieren"
 +$message       = "Alte Dateien archivieren? (Werden am Ursprungsort gelöscht auf ... kopiert)"
 +$buttons       = [System.Windows.Forms.MessageBoxButtons]::OK
 +$icon          = [System.Windows.Forms.MessageBoxIcon]::Question
 +$defaultButton = [System.Windows.Forms.MessageBoxDefaultButton]::Button1
 +$antwort       = [System.Windows.Forms.MessageBox]::Show($message, $title, $buttons, $icon, $defaultButton)
 +if($antwort -eq [System.Windows.Forms.DialogResult]::Yes)
 +{
 +    "OK, die Dateien werden archiviert..."
 +}
 +
 +#endregion
 +#region Debug- und Ausnahme-Management
 +
 +<#
 +    Errors sind OUT Exceptions sind IN
 +     
 +    Reagieren auf Ex. per "trap"-Block oder "try-catch"-Block oder auch nicht!
 + 
 +    WICHTIG: Ex. nur behandeln wenn eine Lösung vorhanden ist (Meldungen sind keine Lösungen)
 +#>
 +
 +$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Continue
 +$WarningPreference     = [System.Management.Automation.ActionPreference]::Continue
 +$VerbosePreference     = [System.Management.Automation.ActionPreference]::SilentlyContinue
 +$InformationPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
 +$DebugPreference       = [System.Management.Automation.ActionPreference]::SilentlyContinue
 +$ConfirmPreference     = [System.Management.Automation.ConfirmImpact]::High
 +
 +#
 +# 1. Ausnahme analysieren (d.h. Ausnahme reproduzieren)
 +#
 +
 +1/0 # Div durch 0
 +$error.Count
 +$error[0] # Die letzte Ausnahme
 +$error[0] -is [System.Management.Automation.RuntimeException]  # Oder...
 +$error[0] -is [System.Management.Automation.ErrorRecord]      
 +$error[0].Exception -is [Exception]
 +
 +# 1. Ebene
 +$error[0].Exception | fl * -Force
 +$error[0].Exception.Message
 +$error[0].Exception.GetType()
 +$error[0].Exception.StackTrace
 +$error[0].Exception.InnerException -eq $null 
 +
 +# 2. Ebene
 +$error[0].Exception.InnerException | fl * -Force
 +$error[0].Exception.InnerException.Message
 +$error[0].Exception.InnerException.GetType()
 +$error[0].Exception.InnerException.StackTrace
 +$error[0].Exception.InnerException.InnerException -eq $null 
 +
 +# Wenn 'Exception.InnerException.InnerException' = null, keine weiteren Ebenen, sonst wie 2. Ebene verfahren
 +
 +
 +# TRAP
 +# Muss vor der Ex aufgestellt werden
 +
 +trap # Alle
 +{
 +}
 +# Code mit Fehler
 +
 +trap [DivideByZeroException]
 +{
 +    # Was soll bei Ex passieren:
 +    "Fehler gefangen für: {0}" -f $_.Exception.Message
 +   continue
 +   #break
 +   #exit
 +}
 +
 +$Erg = (100 / 0)
 +echo $Erg
 +Write-Output "Anweisung nach dem Laufzeitfehler"
 +
 +#
 +# TRY-CATCH-Block
 +# WICHTIG Nur nutzen wenn eine LÖSUNG vorhanden ist!
 +# MsgBox SIND KEINE LÖSUNG!!!!
 +#
 +
 +try
 +{
 +    # Codezeil(en) die evtl. eine Ex. auslösen
 +    $xmlFile = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlx"
 +    $xmlDokument = New-Object -TypeName xml
 +    $xmlDokument.Load($xmlFile)
 +    Write-Host "Alles OK!" # Die letzte Zeile wird nur erreicht wenn keine Ex aufgetreten ist!
 +}
 +catch [System.Net.WebException]
 +{
 +    # Code für WebException
 +}
 +catch # Dieser Catch-Block fängt ALLE Ex ab (ideal für Ex-Analyse, Protokollieren)
 +{
 +    # Die Lösung: In eine Datei loggen...
 +    $_.Exception.GetType()
 +    $_.Exception.InnerException.GetType()
 +    $_.Exception.InnerException.Message
 +    #throw # Ex erneut werfen
 +}
 +
 +# Eigene Ex werfen
 +Write-Error -Message "Meldungstext"
 +
 +
 +#
 +# DEBUGGING
 +#
 +# 1. Nur *.ps1-Dateien können vom DEBUG profitieren
 +# 2. *.ps1-Datei muss vorher gespeichert sein
 +#
 +# F9 => Haltepunkt setzen
 +# F5 => Debugger wird gestartet
 +# Bleibt beim ersten HP stehen
 +# F11 => Führt die nächste Code-Zeile aus (gelbe Zeile wurde NOCH NICHT ausgeführt)
 +# s. ToolTip Variablen
 +# SHIFT + F5 = DEBUG beenden
 +#
 +
 +Get-Command -Noun PSBreakPoint -Module Microsoft.PowerShell.*
 +
 +Write-Debug   -Message "Meldungstext" -Debug
 +Write-Verbose -Message "Meldungstext" -Verbose
 +Write-Warning -Message "Meldungstext"
 +
 +#
 +# Analyse zwischen CmdLets
 +#
 +
 +Trace-Command ParameterBinding  {Get-Alias ls | fl} -PSHost
 +
 +#endregion
 +#region PowerShell-Dateiendungen
 + 
 +# *.ps1 = Windows PowerShell Shell-Skript Version 1.0 -Datei (Beachte Ausführungsrichtlinien)
 +
 +# *.ps1xml = Windows PowerShell Format- und Typdefinitionen -Datei, z.B.:
 +gci C:\Windows\System32\WindowsPowerShell\v1.0\*.ps1xml
 +. C:\Windows\System32\WindowsPowerShell\v1.0\types.ps1xml
 +
 +# *.psc1 = Windows PowerShell Console -Datei
 +Export-Console -Path c:\temp\myconsole.psc1
 +powershell.exe -PSConsoleFile c:\temp\myconsole.psc1
 +
 +# *.psd1 = Windows PowerShell Modul-Manifest -Datei
 +# *.psm1 = Windows PowerShell Modul -Datei (Beachte Ausführungsrichtlinien)
 +# psd1 definiert die Eckdaten eines Modules und psdm1 den Inhalt des Modules, z.B. siehe:
 +gci C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive
 +
 +#endregion
 +#region Ausführungsrichtlinien für Script-Dateien (*.ps1, *.psm1)
 +
 +# In das Thema einlesen
 +Get-Help about_Execution_Ploicys -ShowWindow
 +Get-Help about_Signing           -ShowWindow 
 +
 +# Default Restricted
 +Get-ExecutionPolicy 
 +
 +# Für Test-/Entwicklungszwecke
 +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
 +
 +# Produktivsysteme (Alle *.ps1-Dateien müssen signiert werden)
 +Set-ExecutionPolicy -ExecutionPolicy AllSigned
 +
 +# Einstellung bleiben dauerhaft erhalten
 +# Einstellung kann auch per GPO vergenommen werden
 +# Einstellung kann nur mit Admin-Rechten vorgenommen werden
 +
 +Set-ExecutionPolicy -ExecutionPolicy Unrestricted
 +Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
 +Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser 
 +
 +#
 +# Möglichkeiten *.ps1-Datei auszuführen:
 +#
 +
 +# - GPO
 +# - Aufruf in der Console, z.B. . "c:\temp\test.ps1"
 +# - Per Rechtsklick / Mit PowerShell ausführen
 +# - Per Verknüpfung auf => powershell.exe -File "c:\temp\test.ps1" -WindowStyle Hidden
 +# powershell.exe -File "c:\temp\test.ps1" -NoExit
 +# - Per MyPSScript.exe => z.B. über das Tool PowerGui oder VisualStudio und C#
 +# - Aufruf der *.ps1-Datei in einer der möglichen Autostart-Dateien
 +# - Aufgabenplanung, RUN-Schlüssel, etc.
 +
 +#endregion
 +#region Auto-Start-Script-Dateien
 +
 +# Default-Dateien
 +
 +# WICHTIG: Ordner und Dateien evtl. erstellen
 +
 +# Auswirkung für: LocalMachine
 +$env:windir      + "\system32\WindowsPowerShell\v1.0\profile.ps1"                         # Host, Console, ISE
 +$env:windir      + "\system32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1"    # Console
 +$env:windir      + "\system32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1" # ISE
 +
 +# Auswirkung für: CurrentUser
 +$env:USERPROFILE + "\Documents\WindowsPowerShell\profile.ps1"                         # Host, Console, ISE
 +$env:USERPROFILE + "\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"    # Console
 +$env:USERPROFILE + "\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1" # ISE
 +
 +# z.B. das ständige Ausführen einer *.ps1-Datei
 +Add-Content -Force `
 +    -Path "$env:USERPROFILE\Documents\WindowsPowerShell\profile.ps1" `
 +    -Value ". ""C:\temp\CmdLib.ps1"""
 +
 +#endregion
 +#region Dynamischer Aufruf von *.ps1-Dateien
 +
 +# Test immer in einer ps1-Datei in der Console, nicht in der ISE
 +
 +$aktuellerPfad = $MyInvocation.MyCommand.Definition
 +$aktuellerPfad # C:\Temp\MachWas.ps1
 +
 +$aktuellerOrdner = Split-Path -Path $aktuellerPfad -Parent
 +$aktuellerOrdner # C:\Temp
 +
 +$pfadNeu = Join-Path -Path $aktuellerOrdner -ChildPath "AnderesScript.ps1"
 +$pfadNeu # C:\Temp\AnderesScript.ps1
 +. $pfadNeu
 +
 +#endregion
 +#region Ein- / Ausgabe-Parameter von "*.ps1"
 +
 +# -> Einfach einen Param-Block einfügen (siehe Function)
 +
 +#endregion
 +#region Scripte (*.ps*1) signieren
 +
 +#
 +# 0. VORBEREITUNG
 +#
 +
 +Set-Location C:\Temp
 +Copy-Item -Path "$env:USERPROFILE\OneDrive\Wissen\PowerShell\MakeCert.exe" -Destination .\ -Force # s. MSDN
 +Test-Path -Path .\Get-EuroExchange.ps1                                                            # Vorhanden?
 +Set-ExecutionPolicy -ExecutionPolicy AllSigned -Scope CurrentUser -Force                          # Auch per GPO möglich.
 +Get-ExecutionPolicy -List                                                                         # Check?
 +$CAName          = "_PowerShell Root CA TEMP"                                                     # Zertifizierungsstelle
 +$PSDeveloperName = "_Attila Krick (PS Developer)"                                                 # Der Signierer
 +$PS1Name         = "Get-EuroExchange.ps1"                                                         # Die zu signierende Datei
 +
 +# WARUM?
 +# a) Schutz gegen Manipulation von PS-Scriptdateien (ps1, psm1, ps1xml, ...)
 +# b) Ausführungsrichtlien auf "AllSigned" d.h. PS-Scriptdateien nur noch ausgeführt werden, wenn:
 +# 1. o.a. Dateien X.509 signiert sind.
 +# 2. o.a. X.509 von einer "Vertrauenswürdigen Stammzertifizierungsstelle" abstammt (engl. Root)
 +# 3. o.a. X.509 im Zertifikatspeicher für "Vertrauenswürdige Herausgaber" enthalten ist (engl. TrustPublisher)
 +
 +# Problem ...
 +& "C:\temp\$PS1Name"
 +#Lösung ... Lösung => Signieren!
 +
 +
 +# 1. Vielleicht besitzen Sie schon so ein x.509 Zertifikat mit Verwendungszweck "Codesigning" inkl. des "Private Key"?
 +Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert
 +# ... Wenn vorhanden dan weiter mit Punkt 5.
 +
 +
 +# 2. ROOT-CA erstellen
 +.\MakeCert.exe -n "CN=$CAName" -a sha512 -r -sv "$CAName.pvk" "$CAName.cer"
 +gci .\ | ? Extension -IN '.cer', '.pvk'
 +. ".\$CAName.cer"
 +
 +# 3. Öffentlich Root-CA verteilen (z.B. über GPO) in den Speicher für vertrauenswürdige Stammzertifizierungsstellen (Root)
 +$CACert = Import-Certificate -FilePath "$CAName.cer" -CertStoreLocation Cert:\LocalMachine\Root 
 +gci Cert:\ -Recurse | ? Thumbprint -EQ $CACert.Thumbprint
 +. ".\$CAName.cer"
 +
 +# 4. Signierungszertifikate pro Benutzer in "Eigene Zertifikate (MY)" installieren
 +.\MakeCert.exe -n "CN=$PSDeveloperName" -a sha512 -eku 1.3.6.1.5.5.7.3.3 -pe -ss MY -ic "$CAName.cer" -iv "$CAName.pvk"
 +$PSDeveloperCert = gci Cert:\CurrentUser\My -CodeSigningCert | ? Subject -EQ "CN=$PSDeveloperName"
 +gci Cert:\CurrentUser\My -CodeSigningCert | ? Thumbprint -eq $PSDeveloperCert.Thumbprint | Export-Certificate -Type CERT -FilePath "$PSDeveloperName.cer" -Force 
 +. ".\$PSDeveloperName.cer"
 +
 +# 5. Jetzt Können *.ps1-Dateien signiert werden
 +Set-AuthenticodeSignature -FilePath ".\$PS1Name" -Certificate $PSDeveloperCert -Force -HashAlgorithm SHA512 -IncludeChain all
 +. ".\$PS1Name"
 +
 +# 6. OPTIONAL Das öffentliche Benutzer-Zertifikat von 3. muss z.B. per GPO auf alle betroffenen Host's verteillt
 +Import-Certificate -FilePath "$PSDeveloperName.cer" -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
 +gci Cert:\LocalMachine\TrustedPublisher
 +. ".\$PS1Name"
 +
 +# 7. Kontrollieren (Mit / Ohne eine Änderung an der signierten Datei)
 +Get-Content ".\$PS1Name"
 +Get-AuthenticodeSignature -FilePath ".\$PS1Name" | fl *
 +Start-Process -FilePath ".\$PS1Name"                     # TEST: Einmal Code manipulieren
 +Get-AuthenticodeSignature -FilePath ".\$PS1Name" | fl *  # TEST: Valid?
 +. ".\$PS1Name"                                           # TEST: Wird ausgeführt?
 +
 +# 8. Empfohlene Einstellung für den Entwicklungs-Rechner
 +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
 +
 +# -------------------------------------------------------- AUFRÄUMEN ---------------------------------------------
 +
 +gci Cert:\LocalMachine\TrustedPublisher | ? Subject -EQ "CN=$PSDeveloperName"  | Remove-Item
 +gci Cert:\CurrentUser\My                | ? Subject -EQ "CN=$PSDeveloperName"  | Remove-Item
 +gci Cert:\LocalMachine\Root             | ? Subject -EQ "CN=$CAName"           | Remove-Item
 +gci C:\Temp\                            | ? Extension -IN '.cer', '.pvk'       | Remove-Item
 +Set-ExecutionPolicy -ExecutionPolicy Bypass -Force -Scope CurrentUser
 +
 +#endregion
 +#region eigene Module erstellen (Module)
 +
 +Get-Help about_modules -ShowWindow
 +
 +#
 +# 0. Vorbereitung
 +#
 +
 +$moduleName            = "GFU"
 +$moduleGuid            = "cdca8770-8bd2-48ee-971a-e2456349f3b7"                        # z.B. New-Guid aufrufen
 +$destinationModulePath = "C:\Users\Administrator\Documents\WindowsPowerShell\Modules"  # $env:PSModulePath s.a. region Module
 +
 +#
 +# 1. Modul-Ordner erstellen
 +#
 +
 +$destinationModulePath = Join-Path -Path $destinationModulePath -ChildPath $moduleName
 +New-Item -Path $destinationModulePath -ItemType Directory -Force
 +Set-Location -Path $destinationModulePath
 +
 +#
 +# 2. PSM1-Datei erstellen (Diese Datei wird ausgeführt, wenn das Module geladen wird (Evtl. signieren))
 +#
 +
 +New-Item -Path .\$moduleName.psm1 -ItemType File -Force
 +Get-Content -Path '\\srv00\disk\Raum7\Attila Krick\Get-OldFile.ps1'      | Add-Content .\$moduleName.psm1
 +Get-Content -Path '\\srv00\disk\Raum7\Attila Krick\Get-BigFile.ps1'      | Add-Content .\$moduleName.psm1
 +Get-Content -Path '\\srv00\disk\Raum7\Attila Krick\Get-EuroExchange.ps1' | Add-Content .\$moduleName.psm1
 +"New-Alias -Name gee -Value Get-EuroExchange -Force"                     | Add-Content .\$moduleName.psm1
 +
 +#
 +# 3. PSD1-Datei erstellen (Manifest-Informationen über unser Module)
 +#
 +
 +New-ModuleManifest -Path .\$moduleName.psd1 `
 +                   -RootModule $moduleName `
 +                   -Guid $moduleGuid `
 +                   -Author "Attila Krick" `
 +                   -CompanyName "GFU AG" `
 +                   -Copyright "(c) 2019 by Tata" `
 +                   -Description "Test-Module aus der PowerShell-Schulung - Aufbaukurs" `
 +                   -ModuleVersion "1.0.1.3" `
 +                   -AliasesToExport "gee" `
 +                   -CmdletsToExport "Get-OldFile", "Get-BigFile", "Get-EuroExchange"
 +# Bzgl. ModuleVersion: a.b.c.d => a+b (Neue Feature), c (Bugfix'ing), d (Ausroll-Varianten)
 +Start-Process -FilePath .\$moduleName.psd1
 +
 +#
 +# 4. Check!
 +#
 +
 +Get-ChildItem -Path . -Force -Recurse                           # C:\Users\Administrator\Documents\WindowsPowerShell\Modules\GFU mit GFU.psd1 und GFU.psm1 *.Length > 0
 +Start-Process -FilePath .\$moduleName.psd1                      # Angaben prüfen
 +Start-Process -FilePath .\$moduleName.psm1                      # Evtl. Kommentare entfernen?
 +
 +Get-ExecutionPolicy -List                                       # Process, CurrentUser, LocalMachine = RemoteSigned oder Unrestricted oder Bypass
 +Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
 +
 +Get-Module -Name GFU -ListAvailable                             # Vorhanden ?, ExportedCommands ?
 +Import-Module -Name GFU                                         # Fehlerfrei?
 +Get-Module -Name GFU                                            # Vorhanden ?
 +Remove-Module -Name GFU                                         # Fehlerfrei ?
 +Get-Module -Name GFU                                            # Nicht Vorhanden ?
 +gee -Currency USD                                               # Fehlerfrei ?
 +Get-Module -Name GFU                                            # Vorhanden ?
 +
 +#
 +# siehe auch
 +#
 +
 +Show-Command
 +
 +Get-Command *module* -Module PowerShellGet
 +Publish-Module # => PowerShellGallery
 +
 +#endregion
 +#region UI (ohne G und nur mit PowerShell)
 +
 +# Siehe Thema "Ausgabe"
 +
 +$eingabe = Read-Host -Prompt "Bitte Datum eingeben"
 +$eingabe | Get-Member # System.String
 +$eingabe
 +
 +
 +"12.12.12", "13.12.12", "14.12.12" | Out-GridView -Title "Bitte Datum wählen" -OutputMode Single
 +
 +
 +Show-Command -Name Get-EuroExchange -NoCommonParameter -ErrorPopup | Out-GridView
 +
 +
 +1..100 | % {Start-Sleep -Milliseconds 200 ; Write-Progress -Activity "Bitte warten" -PercentComplete $_  }
 +
 +
 +$title   = "Delete Files"
 +$message = "Do you want to delete the remaining files in the folder?"
 +$yes     = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Deletes all the files in the folder."
 +$no      = New-Object System.Management.Automation.Host.ChoiceDescription "&No" , "Retains all the files in the folder."
 +$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
 +$result  = $host.ui.PromptForChoice($title, $message, $options, 0) 
 +switch ($result)
 +{
 +    0 {"You selected Yes."}
 +    1 {"You selected No."}
 +}
 +
 +#endregion
 +#region .NET-Code direkt nutzen
 +
 +# Siehe auch Tipps und Tricks
 +
 +#
 +# z.B. .NET Framework (eine Art Library) nutzen
 +#
 +
 +[System.Reflection.Assembly]::LoadWithPartialName("System.Diagnostics.Stopwatch") # Lädt ein Assembly aus GAC über den Namespace-Namen
 +
 +$sw = New-Object -TypeName System.Diagnostics.Stopwatch # "StopWatch" => Klasse im Namensraum "System.Diagnostics"
 +$sw.Start()
 +[System.Threading.Thread]::Sleep(2500)
 +$sw.Stop()
 +$sw
 +
 +
 +#
 +# z.B. .NET nutzen => OOP (C# oder VisualBasic)
 +#
 +
 +$source = @"
 +public class Rechner
 +{
 +    public static int Addiere(int a, int b)
 +    {
 +        return (a + b);
 +    }
 +     
 +    public int Multipliziere(int a, int b)
 +    {
 +        return (a * b);
 +    }
 +}
 +"@
 +
 +Add-Type -TypeDefinition $source -Language CSharp # ACHTUNG bis PS5.0 war die Klasse Rechner unveränderbar
 +
 +[Rechner]::Addiere(10, 10)
 +
 +$obj = New-Object -TypeName Rechner
 +$obj.Multipliziere(10, 2)
 +
 +#
 +# AB PowerShell 5.0 auch so:
 +#
 +
 +enum Color
 +{
 +    Blue
 +    Green
 +    Red
 +}
 +$myColor = [Color]::Green
 +$myColor 
 +
 +class Car
 +{
 +    [Color]$Color
 +    [int]$HP
 +}
 +
 +$myCar = New-Object -TypeName Car
 +
 +$myCar.Color = [Color]::Green
 +$myCar.HP = 100
 +$myCar | Get-Member
 +
 +Get-Help -Name about_Class -ShowWindow
 +
 +#endregion
 +#region GUI
 +
 +# Siehe auch TIPPS, TRICKS und BEISPIELE
 +
 +#region GUI ALT (WinForms) nach dem Namespace System.Windows.Forms
 +
 +$url = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
 +$xml = [xml](Invoke-WebRequest -Uri $url | select -exp Content) 
 +$cubes = $xml.Envelope.Cube.Cube.Cube
 +$currencys = $cubes | sort currency | select -exp currency
 +
 +function EuroRateRechnen()
 +{
 +    try
 +    {
 +        $aktWährung = $währungComboBox.SelectedItem
 +        [decimal]$rate = $cubes | ? currency -eq $aktWährung | select -exp rate
 +        $euro = $euroTextBox.Text
 +        $ergebnis = $rate * $euro
 +    }
 +    catch
 +    {
 +        $rate = 0
 +        $ergebnis = 0
 +    }
 +
 +    $eregbnisTextBox.Text = $ergebnis
 +    $rateTextBox.Text = $rate
 +}
 +
 +$währungLabel = New-Object -TypeName System.Windows.Forms.Label
 +$währungLabel.Text = "&Währung:"
 +$währungLabel.Top  = 15
 +$währungLabel.Left = 15
 +
 +
 +$währungComboBox = New-Object -TypeName System.Windows.Forms.ComboBox
 +$währungComboBox.Top                = $währungLabel.Top + $währungLabel.Height
 +$währungComboBox.Left               = 15
 +$währungComboBox.AutoCompleteSource = [System.Windows.Forms.AutoCompleteSource]::ListItems
 +$währungComboBox.AutoCompleteMode   = [System.Windows.Forms.AutoCompleteMode]::SuggestAppend
 +$währungComboBox.Items.AddRange($currencys)
 +$währungComboBox.Add_TextChanged({EuroRateRechnen})
 +$währungComboBox.SelectedIndex      = 0
 +
 +$rateLabel = New-Object -TypeName System.Windows.Forms.Label
 +$rateLabel.Text = "&Rate:"
 +$rateLabel.Top  = $währungComboBox.Top + $währungComboBox.Height + 10
 +$rateLabel.Left = 15
 +
 +$rateTextBox = New-Object -TypeName System.Windows.Forms.TextBox
 +$rateTextBox.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Right
 +$rateTextBox.ReadOnly  = $true
 +$rateTextBox.Top       = $rateLabel.Top + $rateLabel.Height
 +$rateTextBox.Left      = 15
 +$rateTextBox.Text      = "0"
 +
 +$euroLabel = New-Object -TypeName System.Windows.Forms.Label
 +$euroLabel.Text = "&Euro(s):"
 +$euroLabel.Top  = $rateTextBox.Top + $rateTextBox.Height + 10
 +$euroLabel.Left = 15
 +
 +$euroTextBox = New-Object -TypeName System.Windows.Forms.TextBox
 +$euroTextBox.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Right
 +$euroTextBox.Top       = $euroLabel.Top + $euroLabel.Height
 +$euroTextBox.Left      = 15
 +$euroTextBox.Add_TextChanged({EuroRateRechnen})
 +
 +$ergebnisLabel = New-Object -TypeName System.Windows.Forms.Label
 +$ergebnisLabel.Text = "&Ergebnis:"
 +$ergebnisLabel.Top  = $euroTextBox.Top + $euroTextBox.Height + 10
 +$ergebnisLabel.Left = 15
 +
 +$eregbnisTextBox = New-Object -TypeName System.Windows.Forms.TextBox
 +$eregbnisTextBox.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Right
 +$eregbnisTextBox.ReadOnly  = $true
 +$eregbnisTextBox.Top       = $ergebnisLabel.Top + $ergebnisLabel.Height
 +$eregbnisTextBox.Left      = 15
 +
 +$HauptForm = New-Object -TypeName System.Windows.Forms.Form
 +$HauptForm.Text          = "EURO EXCHANGER XP V10.9.1 NT PLUS PROF EDITION LIGHT"
 +$HauptForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
 +$HauptForm.Height        = 300
 +$HauptForm.Controls.AddRange(( `
 +    $währungLabel,  $währungComboBox, `
 +    $rateLabel,     $rateTextBox, `
 +    $euroLabel,     $euroTextBox, `
 +    $ergebnisLabel, $eregbnisTextBox))
 +
 +$euroTextBox.Text        = "1"
 +$HauptForm.ShowDialog()
 +
 +#endregion
 +#region GUI NEU (WPF) nach dem Namespace System.Presentation
 +$url = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
 +$xml = [xml](Invoke-WebRequest -Uri $url | select -exp Content) 
 +$cubes = $xml.Envelope.Cube.Cube.Cube
 +$currencys = $cubes | sort currency | select -exp currency
 +
 +function EuroRateRechnen()
 +{
 +    try
 +    {
 +        $aktWährung    = $WährungenCtrl.SelectedItem
 +        [decimal]$rate = $cubes | ? currency -eq $aktWährung | select -exp rate
 +        $euro          = $eurosCtrl.Text
 +        $ergebnis      = $rate * $euro
 +    }
 +    catch
 +    {
 +        $rate     = 0
 +        $ergebnis = 0
 +    }
 +
 +    $RateCtrl.Text     = $rate
 +    $ErgebnisCtrl.Text = $ergebnis
 +}
 +
 +Add-Type -AssemblyName PresentationFramework
 +Add-Type -AssemblyName System
 +
 +[XML]$xaml = @"
 +<Window
 +    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 +    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 +    Title="€CALC"
 +    Width="220"
 +    Height="255"
 +    WindowStartupLocation="CenterScreen">
 +    <Grid Margin="20">
 +        <StackPanel>
 +            <Label>Währungssymbol</Label>
 +            <ComboBox x:Name="Währungen" />
 +            <abel>Rate</Label>
 +            <TextBox x:Name="Rate" IsReadOnly="True" TextAlignment="Right" />
 +            <Label>Euro(s)</Label>
 +            <TextBox x:Name="Euros" TextAlignment="Right" />
 +            <Label>Ergebnis</Label>
 +            <TextBox x:Name="Ergebnis" IsReadOnly="True" TextAlignment="Right" />
 +        </StackPanel>
 +    </Grid>
 +</Window>
 +"@
 +
 +$reader = New-Object System.Xml.XmlNodeReader $xaml
 +$window = [Windows.Markup.XamlReader]::Load($Reader)
 +
 +$WährungenCtrl = $window.FindName('Währungen')
 +$RateCtrl      = $window.FindName('Rate')
 +$EurosCtrl     = $window.FindName('Euros')
 +$ErgebnisCtrl  = $window.FindName('Ergebnis')
 +
 +$currencys | % { $WährungenCtrl.Items.Add($_) | Out-Null }
 +$WährungenCtrl.Add_SelectionChanged({EuroRateRechnen})
 +$WährungenCtrl.SelectedIndex = 0
 +
 +$EurosCtrl.Add_TextChanged({EuroRateRechnen})
 +$EurosCtrl.Text = "1"
 +
 +$window.ShowDialog()
 +#endregion
 +
 +#endregion
 +
 +#endregion
 +#region ADS
 +
 +#region AGENDA PowerShell für Active Directory-Administratoren
 +
 +Start-Process "http://www.gfu.net/seminare-schulungen-kurse/erweiterungen_sk60/powershell_active_directory_administratoren_s1317.html"
 +
 +# Grundlagen der PowerShell
 +# Was ist PowerShell?
 +# Parsing und PowerShell
 +# Pipelines und Befehle
 +#
 +# Active Directory Verwaltung mit der Powershell
 +# Verwaltung von Active Directory Objekten (OU, User, Computer, Group)
 +# Gruppenrichtlinien in der PowerShell
 +# Anlegen und Verwalten von Password Settings Objects
 +# Anlegen und Verwendung des Active Directory Papierkorbs
 +# Active Directory-Verwaltungscenter
 +# Verwaltung von Active Directory Standorten
 +# ADSI und die Powershell
 +#
 +# Arbeiten mit Typen, Operatoren und Ausdrücke
 +# Array-Operatoren
 +# Flusskontrolle und Funktionen
 +# .NET und WinForms
 +
 +#endregion
 +
 +#region 1. ADS Forest aufsetzen
 +
 +Add-WindowsFeature -Name AD-Domain-Services -IncludeAllSubFeature -IncludeManagementTools -LogPath C:\Users\Administrator\Desktop\AddWinFeature.log
 +
 +Import-Module ADDSDeployment
 +Install-ADDSForest -CreateDnsDelegation:$false `
 +                   -DatabasePath "C:\Windows\NTDS" `
 +                   -DomainMode "Win2012R2" `
 +                   -DomainName "abc.local" `
 +                   -DomainNetbiosName "ABC" `
 +                   -ForestMode "Win2012R2" `
 +                   -InstallDns:$true `
 +                   -LogPath "C:\Windows\NTDS" `
 +                   -NoRebootOnCompletion:$false `
 +                   -SysvolPath "C:\Windows\SYSVOL" `
 +                   -Force:$true `
 +                   -SafeModeAdministratorPassword (Read-Host -AsSecureString)
 +     
 +#endregion
 +#region 2. AD & PS Vorbereitung
 +
 +Update-Help -Module * -UICulture de-DE, en-US -Force
 +
 +#evtl. dcpromo
 +
 +$cred = Get-Credential
 +Add-Computer -DomainName "abc" -Credential $cred
 +
 +#endregion
 +
 +#region Module
 +
 +Get-Module -ListAvailable # Installiert Module?
 +Get-Module                # z.Zt. geladenen Module?
 +
 +Get-WindowsFeature RSAT*
 +Add-WindowsFeature -Name RSAT-AD-PowerShell, RSAT-AD-Tools, RSAT-ADDS # für Windows Server 2012
 +
 +# 1. Für Windows-Client RSAT-Packet (*.msu) installieren
 +# 3. Add-WindowsFeature -Name RSAT-AD-PowerShell
 +
 +Get-Module -Name ActiveDirectory -ListAvailable
 +Import-Module -Name ActiveDirectory
 +Get-Module
 +Get-PSDrive -PSProvider ActiveDirectory # Grund für das manuelle Laden
 +Remove-Module -Name ActiveDirectory
 +
 +#endregion
 +
 +#region Active Directory-Objekte
 +
 +Get-Command -Module ActiveDirectory | Out-GridView
 +
 +New-ADOrganizationalUnit -Name "AK" -Path "DC=abc, DC=local"
 +
 +Show-Command New-ADUser
 +New-ADUser -Name sauer2 -Path "OU=AK, DC=abc, DC=local"
 +$ms = New-ADUser -Name mittelsauer -Path "OU=AK, DC=abc, DC=local" -PassThru
 +$ms | Get-Member
 +
 +New-ADUser -Name sauer5 -Path "OU=AK, DC=abc, DC=local" -Enabled $true -AccountPassword (ConvertTo-SecureString -String "P@ssw0rd" -AsPlainText -Force)
 +
 +# 1. Anmeldung Pwd ändern
 +# 2. Anmeldename fehlt
 +# 3. Job-Titel fehlt
 +
 +New-ADUser -Name sauer6 -UserPrincipalName "sauer6" `
 +                        -ChangePasswordAtLogon $true `
 +                        -Title "Supporter" `
 +                        -Path "OU=AK, DC=abc, DC=local" -Enabled $true -AccountPassword (ConvertTo-SecureString -String "P@ssw0rd" -AsPlainText -Force)
 +
 +#
 +# Benutzer über CSV hinzufügen
 +#
 +
 +$csv = @"
 +UserName;InizialPassword
 +Uwe;P@ssw0rd
 +Inge;P@ssw0rd
 +Horst;P@ssw0rd
 +"@
 +Set-Content -Path c:\temp\NewUsers.csv -Value $csv
 +
 +Import-Csv -Path C:\temp\NewUsers.csv -Delimiter ";" | ForEach-Object {
 +    New-ADUser -Name $_.UserName `
 +               -UserPrincipalName $_.UserName `
 +               -Path "OU=AK, DC=abc, DC=local" `
 +               -Enabled $true -AccountPassword (ConvertTo-SecureString -String $_.InizialPassword `
 +                                                                       -AsPlainText `
 +                                                                       -Force)
 +}
 +
 +$FormatEnumerationLimit = -1
 +
 +Get-ADUser -Identity "lustig" | Get-Member
 +(Get-ADUser -Identity "lustig").gettype()
 +
 +
 +$Identity = "lustig"
 +$InitPassword = [System.IO.Path]::GetRandomFileName() -replace "\.", "@"
 +$bx = Get-ADUser -Identity $Identity | Set-ADAccountPassword -Reset -NewPassword (ConvertTo-SecureString $InitPassword -A -f) -PassThru
 +$bx | Set-ADUser -ChangePasswordAtLogon $true -AccountExpirationDate (Get-Date).AddMinutes(3)
 +
 +function Reset-ADUserPassword
 +{
 +    <#
 +        .SYNOPSIS
 +            bla bla bla
 +         
 +        .DESCRIPTION
 +            hmmm hmmm hmm
 + 
 +        .EXAMPLE
 +            Reset-ADUserPassword -Identity "lustig"
 +            bla bla bla
 + 
 +    #>
 +    param
 +    (
 +        # Geben Sie .....
 +        [Parameter(Mandatory=$true)]
 +        [string]$Identity
 +    )
 +    $InitPassword = [System.IO.Path]::GetRandomFileName() -replace "\.", "@0G"
 +    $bx = Get-ADUser -Identity $Identity | Set-ADAccountPassword -Reset -NewPassword (ConvertTo-SecureString $InitPassword -A -f) -PassThru
 +    $bx | Set-ADUser -ChangePasswordAtLogon $true -AccountExpirationDate (Get-Date).AddMinutes(3)
 +    [pscustomobject]@{"Inizial Passwort für die erste Anmeldung"=$InitPassword}
 +}
 +
 +Reset-ADUserPassword -Identity "lustig" | Out-GridView
 +Show-Command Reset-ADUserPassword  -ErrorPopup
 +
 +#
 +# AD Gruppen
 +#
 +
 +Get-ADGroup -Identity administratoren
 +
 +$a = Get-ADGroup -Identity S-1-5-32-544 -Properties member
 +$a.member | Get-Member
 +$a.member[0]
 +$a.member | Get-ADObject
 +Get-ADGroup -Filter 'GroupCategory -eq "Security" -and GroupScope -ne "DomainLocal"'
 +Get-ADGroup -Server localhost  -Filter {GroupScope -eq "DomainLocal"} -SearchBase "DC=abc,DC=local"
 +
 +$zeroGrps = @()
 +$a = Get-ADGroup -Properties member -Filter *
 +foreach ($item in $a)
 +{
 +    if($item.member.Count -eq 0)
 +    {
 +        $zeroGrps += $item
 +    }
 +}
 +$filename = "LeereGruppen_vom_{0:yyyy-MM-dd}.html" -f (Get-Date)
 +$zeroGrps | ConvertTo-Html -Property Name -PreContent "Leere AD-Gruppen vom $(Get-Date)" | Out-File ".\$filename"
 +
 +# vs.
 +
 +Get-ADGroup -Properties member -Filter * | 
 +    where {$_.member.Count -eq 0} | 
 +    ConvertTo-Html -Property Name -PreContent "Leere AD-Gruppen vom $(Get-Date)"
 +
 +Get-ADUser -Identity Administrator
 +
 +Get-Command -Name *group* -Module ActiveDirectory 
 +
 +# Wo ist der Administrator mitglied?
 +Get-ADPrincipalGroupMembership -Identity Administrator | ft Name 
 +
 +# Wo ist Wer mitglied?
 +Get-ADUser -Filter * | ForEach-Object {
 +    $user = $_
 +    $_ | Get-ADPrincipalGroupMembership | ForEach-Object {
 +        [pscustomobject]@{Username=$user.Name;GoupName=$_.Name}
 +    }
 +} | ConvertTo-Html | Out-File .\UsersGroups.html
 + 
 +#endregion
 +
 +Get-ADForest
 +[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
 +
 +#region ADSI & LDAP
 +
 +Start-Process http://www.powershellpraxis.de/index.php/active-directory/activedirectory-provider
 +
 +$rootDSE = [ADSI]"LDAP://rootDSE"
 +$adRootPath = $rootDSE.defaultNamingContext
 +$ad = [ADSI]"LDAP://$adRootPath"
 +
 +$adComputer = [ADSI]'LDAP://CN=Computers,DC=abc,DC=local' 
 +$adComputer.Children
 +
 +$User = [ADSI]"LDAP://cn=lustig, ou=ak, dc=abc, dc=local"
 +$UAC = $User.UserAccountControl[0] -bor 65536                                                                                             # Password never expire
 +$User.Put("userAccountControl",$UAC)
 +$User.SetInfo()
 +
 +
 +$admin = [ADSI]"LDAP://cn=Administrator,cn=Users,dc=abc,dc=local"
 +$admin.class
 +$admin.objectclass
 +
 +$admin = [ADSI]"LDAP://cn=Administrator,cn=Users,dc=abc,dc=local"
 +$groups = $admin.MemberOf | ForEach-Object {[ADSI]"LDAP://$_"
 +$groups
 +
 +([ADSI]'WinNT://lunar80/Administratoren,group').Add('WinNT://DOMAIN/USER,user'                                                           # Add a domain user to a remote server local group, if your current user has admin over the remote machine
 +([ADSI]'WinNT://lunar80,computer').psbase.children | where { $_.psbase.schemaClassName -eq 'group' } | foreach { ($_.name)[0]}              # Get all local groups on a remote server
 +([ADSI]'WinNT://lunar80/Administratoren,group').psbase.Invoke('Members') | foreach { $_.GetType().InvokeMember('ADspath', 'GetProperty', $null, $_, $null).Replace('WinNT://', '') }  # Find members of the local Administrators group on a remote server
 +
 +$a=([ADSI]'WinNT://lunar80/Administrator,user');
 +$a.UserFlags=2; # Enable the local Administrator account on a remote server
 +$a.CommitChanges() 
 + 
 +$a=([ADSI]'WinNT://SERVER/Administrator,user');
 +$a.UserFlags=512; # Disable the local Administrator account on a remote server
 +$a.CommitChanges()
 +
 +Start-Process https://technet.microsoft.com/en-us/library/ff730967.aspx
 +
 +Get-ADObject -LDAPFilter "(&(operatingSystem=Windows Server 2012 R2 Datacenter)(objectClass=computer))" -SearchBase "dc=abc,dc=local" -SearchScope Subtree
 +Search-ADAccount -PasswordExpired -UsersOnly -SearchBase "dc=abc,dc=local" -SearchScope OneLevel 
 +
 +$adSearcher = New-Object -TypeName DirectoryServices.DirectorySearcher
 +$adSearcher.Filter = "(objectCategory=user)"
 +$adSearcher.SearchRoot = "LDAP://DC=abc,DC=local"
 +$result = $adSearcher.FindAll()
 +$result | Get-Member
 +$result | fl *
 +
 +$ACCOUNTDISABLE       = 0x000002
 +$DONT_EXPIRE_PASSWORD = 0x010000
 +$PASSWORD_EXPIRED     = 0x800000
 +$searcher = [adsisearcher]"(&(objectClass=user)(objectCategory=person))"
 +$searcher.FindAll() | ForEach-Object {
 +  $user = [adsi]$_.Properties.adspath[0]
 +  [PSCustomObject]@{
 +    SamAccountName       = $user.sAMAccountName[0]
 +    Name                 = $user.name[0]
 +    Mail                 = $user.mail[0]
 +    PasswordLastSet      = [DateTime]::FromFileTime($_.Properties.pwdlastset[0])
 +    Enabled              = -not [bool]($user.userAccountControl[0] -band $ACCOUNTDISABLE)
 +    PasswordNeverExpires =      [bool]($user.userAccountControl[0] -band $DONT_EXPIRE_PASSWORD)
 +    PasswordExpired      =      [bool]($user.userAccountControl[0] -band $PASSWORD_EXPIRED)
 +  }
 +}
 +
 +#
 +# ODER per ActiveDirectory-Module:
 +#
 +
 +Import-Module ActiveDirectory
 +$attributes = 'SamAccountName', 'Name', 'Mail', 'PasswordLastSet', 'Enabled','PasswordNeverExpires', 'PasswordExpired'
 +Get-ADUser -Filter * -Properties $attributes | select $attributes
 +
 +#endregion
 +#region GPO
 +
 +Import-Module GroupPolicy -Verbose 
 +Get-Command -Module GroupPolicy
 +
 +Backup-GPO                 # Mit diesem Cmdlet sichert man das angegebene Gruppenrichtlinienobjekt (GPO) oder alle Gruppenrichtlinienobjekte in einer angegebenen Domäne in ein Sicherungsverzeichnis. Dabei muss das Sicherungsverzeichnis bereits existieren.
 +Restore-GPO                # Dieses Cmdlet stellt eine GPO-Sicherung in der Domäne wieder her, in der sie ursprünglich gespeichert wurde. Ist die ursprüngliche Domäne oder die GPO nicht mehr in der Domäne vorhanden, tritt ein Fehler auf.
 +Get-GPO                    # Eine bestimmte GPO oder alle GPOs innerhalb einer Domäne kann man sich mit diesem Cmdlet anzeigen lassen.
 +Copy-GPO                   # Dieses Cmdlet erstellt eine neue GPO und kopiert die Einstellungen aus der Quell-GPO in die neue GPO. Mit diesem Cmdlet kann eine GPO aus einer Domäne in eine andere Domäne innerhalb der gleichen Gesamtstruktur kopiert werden.
 +New-GPO                    # Eine neue GPO wird mit diesem Cmdlet erstellt.
 +Remove-GPO                 # Eine GPO wird mit samt allen Verlinkungen, mit diesem Cmdlet gelöscht.
 +Import-GPO                 # Dieses Cmdlet importiert die Einstellungen aus einer gesicherten GPO in die angegebene Ziel-GPO. Dabei kann sich das Ziel-GPO in einer anderen Domäne oder in einer anderen Gesamtstruktur befinden als die Sicherungs-GPO und muss vorher nicht existieren.
 +Rename-GPO                 # Mit diesem Cmdlet kann man eine GPO umbenennen bzw. der GPO einen anderen Anzeigenamen zuweisen. Dabei bleibt die GUID der umbenannten GPO erhalten.
 +Get-GPInheritance          # Informationen zur Gruppenrichtlinienvererbung für eine angegebene Domäne oder Organisationseinheit kann man sich mit diesem Cmdlet ausgeben lassen.
 +Get-GPOReport              # Hiermit wird ein Bericht im XML- oder HTML-Format generiert, in dem die Eigenschaften und Richtlinieneinstellungen für eine angegebene GPO oder für alle GPOs einer Domäne angezeigt werden. Dieses Cmdlet eignet sich ideal zu Dokumentationszwecken. Möchte man alle Richtlinieneinstellungen aller GPOs innerhalb der Domäne in eine HTML Datei exportieren, so gilt es diesen Befehl auszuführen: Get-GPOReport -All -Domain <Domäne.de> -ReportType HTML -Path C:\GPOReport\GPOReport.html. Das Zielverzeichnis muss bereits existieren, sonst erhält man eine Fehlermeldung.
 +Get-GPPermissions          # Die Berechtigungen für einen oder mehrere Sicherheitsprinzipale kann man mit diesem Cmdlet in der angegebenen GPO abrufen.
 +Get-GPPrefRegistryValue    # Dieses Cmdlet zeigt eine oder mehrere Registrierungseinstellungen die unterhalb der Computerkonfiguration oder der Benutzerkonfiguration in einer GPO getätigt wurden an.
 +Get-GPRegistryValue        # Bei diesem Cmdlet werden eine oder mehrere registrierungsbasierte Richtlinieneinstellungen aus der Computerkonfiguration oder der Benutzerkonfiguration in einer GPO abgerufen.
 +Get-GPResultantSetOfPolicy # Mit diesem Cmdlet kann man die Richtlinienergebnissatz-Informationen für einen Benutzer, einen Computer oder für beide in eine Datei im HTML- oder XML-Format ausgeben lassen.
 +Get-GPStarterGPO           # Ein bestimmtes oder alle Starter-GPOs werden mit diesem Cmdlet angezeigt.
 +New-GPLink                 # Eine GPO wird auf eine Organisationseinheit (OU), einen AD-Standort oder auf die Domäne mit diesem Cmdlet verlinkt.
 +New-GPStarterGPO           # Mit diesem Cmdlet wird eine neue Starter-GPO erstellt.
 +Remove-GPLink              # Dieses Cmdlet entfernt eine GPO-Verklinkung von einer OU, einem AD-Standort oder von der Domäne.
 +Remove-GPPrefRegistryValue # Eine oder mehrere Registrierungseinstellungen werden aus der Benutzerkonfiguration oder Computerkonfiguration innerhalb einer GPO mit diesem Cmdlet entfernt.
 +Remove-GPRegistryValue     # Um eine oder mehrere Registrierungsbasierte Richtlinieneinstellungen aus der Benutzerkonfiguration oder Computerkonfiguration innerhalb einer GPO zu entfernen, muss dazu dieses Cmdlet verwendet werden.
 +Set-GPInheritance          # Die Vererbung einer GPO kann mit diesem Cmdlet deaktiviert werden. Oder die Deaktivierung der Vererbung für eine angegebene OU oder Domäne lässt sich ebenfalls mit diesem Cmdlet aufheben.
 +Set-GPLink                 # Die Eigenschaften einer GPO-Verknüpfung lassen sich mit diesem Cmdlet festlegen.
 +Set-GPPermissions          # Die Berechtigungen einer GPO oder für alle GPOs innerhalb einer Domäne lassen sich mit diesem Cmdlet bearbeiten.
 +Set-GPPrefRegistryValue    # Dieses Cmdlet konfiguriert eine Registrierungseinstellung unter der Benutzerkonfiguration oder der Computerkonfiguration in einer GPO.
 +Set-GPRegistryValue        # Mit diesem Cmdlet konfiguriert man eine oder mehrere registrierungsbasierte Richtlinieneinstellungen unter der Benutzerkonfiguration oder der Computerkonfiguration in einer GPO.
 +
 +Get-GPO -Name "Default Domain Policy" 
 +Get-GPO -Name "Default Domain Policy" | Get-GPOReport -ReportType Html | Out-File -Encoding utf8 -FilePath .\ddp_report.html 
 +
 +# Kennwort muss Komplexitätsvoraussetzungen entsprechen?
 +$xmlDoc = [xml](Get-GPO -Name "Default Domain Policy" | Get-GPOReport -ReportType Xml)
 +$xmlDoc.GPO.Computer.ExtensionData.Extension.Account | ? Name -EQ PasswordComplexity | select -ExpandProperty SettingBoolean
 +
 +# Oder nativ in den Richtlinien lesen
 +$gpo = Get-GPO -Name "Default Domain Policy" 
 +gci "\\lunar80\SYSVOL\abc.local\Policies\{$($gpo.Id)}\MACHINE\"
 +
 +#endregion
 +#region Password Settings Objects
 +
 +New-ADFineGrainedPasswordPolicy -Name "DomainUsersPSO" -Precedence 500 -ComplexityEnabled $true -Description "The Domain Users Password Policy" -DisplayName "Domain Users PSO" -LockoutDuration "0.12:00:00" -LockoutObservationWindow "0.00:15:00" -LockoutThreshold 10 
 +Set-ADFineGrainedPasswordPolicy -Identity ‘DomainUsersPSO’ -Replace @{‘msDS-PSOAppliesTo’=’CN=PSOTest, OU=AK, DC=abc, DC=local’}
 +
 +$allPSOUsers = Get-ADFineGrainedPasswordPolicySubject "DomainUsersPSO"
 +               ? {$_.objectClass -eq "group"}| 
 +               % {Get-ADGroupMember $_.Name -Recursive} | 
 +               ? {$_.objectClass -eq "user"} | 
 +               select -Unique
 +
 +$allUsers = Get-AdUser -Filter *
 +
 +$allUsersNotinPSO = Compare-Object -ReferenceObject $allUsers -DifferenceObject $allPSOUsers -PassThru | select Name
 +$allUsersNotinPSO
 +
 +#endregion
 +#region Active Directory Papierkorbs
 +
 +Get-Command -Noun ADOptionalFeature
 +
 +Get-ADOptionalFeature -Filter *
 +
 +Enable-ADOptionalFeature -Identity "Recycle Bin Feature" -Scope ForestOrConfigurationSet -Target "abc.local"
 +
 +Get-ADObject -Filter {name -like "Inge*"} –IncludeDeletedObjects
 +Get-ADObject -Filter {name -like "Inge*" -and Deleted -eq $true} –IncludeDeletedObjects | Restore-ADObject
 +
 +#endregion
 +#region Active Directory-Verwaltungscenter
 +
 +# Siehe "Windows PowerShell-Verlauf History"
 +
 +"$env:windir\system32\dsac.exe"
 +
 +# siehe auch: https://technet.microsoft.com/de-de/library/jj574144(v=ws.11).aspx
 +
 +#endregion
 +#region Active Directory Standorten
 +
 +Get-Command "*ADReplication*" -Module ActiveDirectory
 +
 +New-ADReplicationSite -Name "Würzburg"
 +
 +$Schedule = New-Object -TypeName System.DirectoryServices.ActiveDirectory.ActiveDirectorySchedule
 +$Schedule.ResetSchedule()
 +$Schedule.SetDailySchedule("Twenty","Zero","TwentyTwo","Thirty");
 +New-ADReplicationSite -Name "München" -ReplicationSchedule $schedule
 +
 +New-ADReplicationSiteLink -Name "Würzburg-München" -SitesIncluded Würzburg, München -Cost 100 -ReplicationFrequencyInMinutes 15 -InterSiteTransportProtocol IP
 +
 +New-ADReplicationSubnet -Name "192.168.50.0/24" -Site (Get-ADReplicationSite -Identity "Würzburg")
 +New-ADReplicationSubnet -Name "192.168.51.0/24" -Site (Get-ADReplicationSite -Identity "München")
 +
 +Get-ADReplicationSite -Filter *
 +Get-ADReplicationSiteLink -Filter *
 +Get-ADReplicationSubnet -Filter *
 +
 +## Alle DC-Server an einem Standort
 +$serverContainerDN = “CN=Servers, CN=Default-First-Site-Name, CN=Sites, CN=Configuration, DC=abc, DC=local”
 +Get-ADObject -SearchBase $serverContainerDN -SearchScope OneLevel -Filter "ObjectClass -eq 'server'" -Properties “DNSHostName”, “Description” | Select Name, DNSHostName, Description
 +
 +# Get replication metadata for the attributes of a group
 +Get-ADReplicationAttributeMetadata -Object "CN=Domänen-Admins, CN=Users, DC=abc, DC=local" -Server localhost -ShowAllLinkedValues
 +
 +# Get filtered replication metadata for all groups
 +Get-ADObject -Filter 'objectclass -eq "group"'
 +    Get-ADReplicationAttributeMetadata -Server localhost | 
 +    Where-Object {$_.LastOriginatingChangeTime -like "*2017*"} | 
 +    Format-Table object
 +
 +Get-ADReplicationConnection -Filter *
 +
 +Get-ADReplicationFailure -Target localhost
 +
 +#endregion
 +#region Zeigt vereinfacht ob die Rechte-Vererbung aktiviert ist oder nicht:
 +
 +$result = gci -Path 'C:\Program Files' -Recurse -Force -Directory -ErrorAction SilentlyContinue | % {
 +    $acl = $_.FullName | Get-Acl
 +    [PSCustomObject]@{
 +        IsInherited    = $acl.Access[0].IsInherited; # ACHTUNG !!!!
 +        Name           = $_.Name;
 +        FullName       = $_.FullName;
 +        DeleteMe       = $false;
 +        AccessToString = $acl.AccessToString;
 +    }
 +}
 +
 +$result | % {
 +    if($_.IsInherited)
 +    {
 +        $parenPath = Split-Path -Path $_.FullName -Parent
 +        $parent = $result | ? { $_.FullName -eq $parenPath -and $_.DeleteMe -eq $false} 
 +        if($parent.IsInherited)
 +        {
 +            $_.DeleteMe = $true
 +        }
 +    }
 +}
 +
 +$result | Out-GridView
 +$result | ? DeleteMe -EQ $false | Out-GridView
 +
 +#endregion
 +
 +#endregion
 +#region Microsoft SQL Server
 +
 +Set-Location -Path 'C:\Program Files\Microsoft SQL Server\MSSQL12.SS2014\MSSQL'
 +Get-Service MSSQLSERVER, SQLSERVERAGENT | Start-Service
 +
 +Import-Module -Name SqlServer # früher SQLPS
 +Get-Command -Module SqlServer
 +
 +Get-ChildItem SQLSERVER:\SQL\AK4GFU\DEFAULT\Databases\AdventureWorks2014\Tables\Person.Person
 +Invoke-Sqlcmd -Database AdventureWorks2014 -Query "SELECT TOP 10 * FROM Person.Person"
 +
 +#endregion
 +#region TIPPS, TRICKS und BEISPIELE
 +
 +#region BASICS: Datum & Zeit
 +#region Eine Dauer von einem Datum subtrahieren, um ein Vergangenheits-Datum zu berechnen
 +
 +$dauer = New-TimeSpan -Days 100 -Hours 31 -Minutes 12
 +$dauer
 +(Get-Date).Subtract($dauer)
 +
 +#endregion
 +#endregion
 +#region PowerShell
 +#region Neuerungen der PowerShell 5.0
 +
 +# Alle Details zur 5.0 Version unter:
 +Get-Help about_Windows_PowerShell_5.0 -ShowWindow
 +Start-Process http://msdn.microsoft.com/de-de/powershell/scripting/whats-new/what-s-new-in-windows-powershell-50
 +
 +#region Get-ItemPropertyValue
 +
 +#NEU: Get-ItemPropertyValue
 +#FRÜHER:
 +(Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name ApplicationBase).ApplicationBase
 +#JETZT:
 +Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name ApplicationBase
 +
 +#endregion
 +
 +#region Einfache String-Operationen
 +"Hallo Welt" | ConvertFrom-String
 +"Hallo Welt" | ConvertFrom-String -Delimiter "ll"
 +"Hallo Welt" | ConvertFrom-String -PropertyNames FirstWord, SecondWord
 +
 +"Lee Holmes", "Steve Lee", "Jeffrey Snover" | Convert-String -Example "Bill Gates=Gates, B.","John Smith=Smith, J."
 +
 +"Hallo Welt" | Format-Hex
 +#endregion
 +
 +#region ZIP-Archive
 +
 +#
 +#Test-Daten erzeugen
 +#
 +New-Item -Path C:\temp\ZipMich -ItemType Directory -Force
 +1..1MB -join ";" | Set-Content -Path "C:\temp\ZipMich\LogFile1.txt" -Force
 +1..1MB -join ";" | Set-Content -Path "C:\temp\ZipMich\LogFile2.txt" -Force
 +
 +#
 +# Compress-Archive
 +#
 +"C:\temp\ZipMich" | Compress-Archive -DestinationPath C:\temp\ZipMich.zip -CompressionLevel Optimal -Force
 +Get-Help Compress-Archive -ShowWindow
 +
 +#
 +# Expand-Archive
 +#
 +"C:\temp\ZipMich.zip" | Expand-Archive -DestinationPath C:\temp\Backup -Force
 +Get-Help Expand-Archive -Full
 +
 +#endregion
 +
 +#region Software-Installation
 +
 +Set-ExecutionPolicy -ExecutionPolicy AllSigned
 +
 +Get-Command -Module PowerShellGet, PackageManagement
 +
 +Find-Package | Out-GridView
 +Install-Package -Name AKPT -Force
 +Get-Module -ListAvailable 
 +Get-Command * -Module AKPT
 +Get-AKAbout
 +Uninstall-Package -Name AKPT
 +
 +Register-PSRepository -Name "myNuGetSource" –SourceLocation "https://www.myget.org/F/powershellgetdemo/api/v2" -PublishLocation "https://www.myget.org/F/powershellgetdemo/api/v2/Packages" -InstallationPolicy Trusted
 +Get-PSRepository
 +Unregister-PSRepository -Name "myNuGetSource"
 +
 +#endregion
 +
 +#region Kryptographie
 +
 +Get-Command -Module Microsoft.PowerShell.Security 
 +
 +$MyCertInf = @"
 +[Version]
 +Signature = "$Windows NT$"
 + 
 +[Strings]
 +szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
 +szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"
 + 
 +[NewRequest]
 +Subject = "cn=me@example.com"
 +MachineKeySet = false
 +KeyLength = 2048
 +KeySpec = AT_KEYEXCHANGE
 +HashAlgorithm = Sha1
 +Exportable = true
 +RequestType = Cert
 + 
 +KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE"
 +ValidityPeriod = "Years"
 +ValidityPeriodUnits = "1000"
 + 
 +[Extensions]
 +2.5.29.37="{text}1.3.6.1.4.1.311.80.1"
 +"@
 +Set-Content -Path C:\temp\MyCert.inf -Value $MyCertInf
 +CertReq -new C:\temp\MyCert.inf C:\temp\MyCert.cer
 +$cert = gci Cert:\CurrentUser\My | ? Subject -EQ "cn=me@example.com"
 +
 +$crypt = "Hallo Welt" | Protect-CmsMessage -To $cert
 +Unprotect-CmsMessage -Content $crypt -To $cert
 +
 +#endregion
 +
 +#region OOP
 +
 +Get-Help about_Classes -ShowWindow
 +
 +enum Farbe
 +{
 +    Blau
 +    Grün
 +    Rot
 +}
 +$meineFarbe = [Farbe]::Grün
 +$meineFarbe 
 +
 +class Auto
 +{
 +    [Farbe]$Farbe
 +    $PS
 +}
 +
 +$meinAuto = New-Object -TypeName Auto
 +
 +$meinAuto.Farbe = [Farbe]::Grün
 +$meinAuto.PS = 100
 +$meinAuto | Get-Member
 +
 +#endregion
 +
 +#region Weitere neue CmdLetds
 +
 +Set-Clipboard -Value "Hallo Köln!"
 +Get-Clipboard
 +
 +Clear-RecycleBin -DriveLetter c: -Confirm:$false
 +
 +New-TemporaryFile
 +
 +New-Guid
 +
 +# symbolischer Verknüpfungen
 +New-Item -ItemType SymbolicLink -Name MySymLinkDir -Target $pshome 
 +
 +# -Depth 2
 +Get-ChildItem c:\ -Recurse -Depth 2 -Force
 +
 +#endregion
 +
 +#region DataCenter Abstraction Layer (DAL)
 +
 +<#
 +    Mit dieser Technologie können Sie direkt auf bestimmte
 +    Netzwerkkomponenten wie Switches und Router zugreifen.
 + 
 +    Dazu muss die Hardware diese Technik aber auch unterstützen.
 +    In diesem Bereich spielen vor allem
 +    Cisco und Huawei eine wichtige Rolle.
 +#>
 +
 +$Session = New-CimSession -ComputerName "NetworkSwitch08"
 +Get-NetworkSwitchFeature -CimSession $Session
 +
 +<#
 +    Name IsEnabled InstanceID PSComputerName
 +    ---- --------- ---------- --------------
 +    SSH True Contoso:Feature:2 10.19.26.49
 +    Tacacs True Contoso:Feature:3 10.19.26.49
 +    BGP False Contoso:Feature:4 10.19.26.49
 +    VLAN True Contoso:Feature:5 10.19.26.49
 +    LACP True Contoso:Feature:6 10.19.26.49
 +    DHCP False Contoso:Feature:7 10.19.26.49
 +    LLDP True Contoso:Feature:8 10.19.26.49
 +#>
 +
 +Get-Help Get-NetworkSwitchFeature -Full
 +
 +Get-Command -Module NetworkSwitchManager | Out-GridView
 +
 +#endregion
 +
 +#region Open Data Protocol
 +
 +<# Das Open Data Protocol, kurz OData ist ein von Microsoft veröffentlichtes HTTP-basiertes Protokoll
 +   für den Datenzugriff zwischen kompatiblen Softwaresystemen. Aufbauend auf älteren Protokollen wie
 +   ODBC und JDBC kann OData u.a. innerhalb von Cloud-Diensten (Azure), MySQL, Java und Rails
 +   eingebunden werden und ist in der Lage, in der Client-Server-Kommunikation eine einheitliche Semantik
 +   für den Datenaustausch zur Verfügung zu stellen.
 +#>
 +
 +Export-ODataEndpointProxy -Uri 'http://services.odata.org/v3/(S(snyobsk1hhutkb2yulwldgf1))/odata/odata.svc' `
 +                          -MetadataUri 'http://services.odata.org/v3/(S(snyobsk1hhutkb2yulwldgf1))/odata/odata.svc/$metadata' `
 +                          -AllowUnsecureConnection `
 +                          -OutputModule C:\Temp\GeneratedScript.psm1 `
 +                          -ResourceNameMapping @{Products = 'Merchandise'}
 +
 +#endregion
 +
 +#region Optimierte Unterstützung für 'Desired State Configuration' (DSC)
 +
 +<#
 +    Weitere Neuerungen in der PowerShell betreffen die mit der
 +    PowerShell 4.0 eingeführte Technologie Desired State Configuration (DSC).
 +     
 +    Hauptsächlich gibt es neue Optionen um festzulegen auf wievielen Computern
 +    gleichzeitig die Änderungen implementiert werden sollen.
 +      
 +    Mit dem Modul 'PowerShellGet' können Sie DSC-Ressourcen in der
 +    'PowerShell Resource Gallery' nutzen, installieren oder hochladen.
 +#>
 +
 +#endregion
 +
 +#endregion
 +#region Escape characters, Delimiters and Quotes
 +
 +http://ss64.com/ps/syntax-esc.html
 +
 +"Hallo `n Köln!" # z.B.
 +
 +#endregion
 +#region Konsolen-Ein-/Ausgabe protokollieren
 +
 +Start-Transcript -Path C:\Temp\PowerShellConsole.log
 +Get-Process | Stop-Process -Force -WhatIf
 +Remove-Item c:\ -Recurse -Force -WhatIf
 +Stop-Transcript
 +Get-Content -Path C:\Temp\PowerShellConsole.log
 +
 +#endregion
 +#region Wartezeit visualisieren
 +for ($i = 1; $i -le 100; $i++)
 +
 +    [System.Threading.Thread]::Sleep(100)
 +    Write-Progress -Activity "Bitte warten" -PercentComplete $i
 +
 +    if($i -ge 20)
 +    {
 +        Write-Progress -Activity "Bitte warten" -PercentComplete $i -Status "Gleich geschaft..."
 +    }
 +}
 +
 +#endregion
 +#region max. Anzeige von Enumerationen (-1 ohne Grenze, Default: 4)
 +Get-Command -Name ForEach-Object | select -First 1 | fl * # Siehe Eigenschaft: Parameters
 +$FormatEnumerationLimit = 4
 +Get-Command -Name ForEach-Object | select -First 1 | fl * # Siehe Eigenschaft: Parameters
 +#endregion
 +#region PowerShell-Transaktion (*-Transaction)
 +
 +Get-Command -Noun Transaction -Module Microsoft.PowerShell.*
 +
 +# Gilt nur für CmdLets die den Parameter UseTransaction besitzen
 +Get-Help * -Parameter UseTransaction 
 +
 +#
 +# BEISPIELE
 +#
 +
 +Set-Location -Path hkcu:\software
 +
 +Start-Transaction
 +New-Item _ABC -UseTransaction
 +New-ItemProperty -Path _ABC -name Heute -value (Get-Date) -UseTransaction
 +Complete-Transaction
 +
 +Get-ItemProperty -Path _ABC
 +Remove-Item -Path _ABC -Force
 +
 +Start-Transaction
 +New-Item _ABC -UseTransaction
 +New-ItemProperty -Path _ABC -name Heute -value (Get-Date) -UseTransaction
 +Undo-Transaction
 +
 +Get-ItemProperty -Path _ABC
 +
 +#endregion
 +#region Job's
 +
 +# Lang anhaltene Aufgaben in Jobs auslagern und Session schließen
 +
 +Start-Job -Name MyJob -ScriptBlock {[System.Threading.Thread]::Sleep(2000); Get-Process}
 +Get-Job
 +Receive-Job -Name MyJob
 +Stop-Job -Name MyJob
 +Wait-Job -Name MyJob
 +Remove-Job -Name MyJob
 +
 +#endregion
 +#region ScriptBlock-Code in eine EXE kompelieren
 +
 +Set-Location C:\temp
 +Remove-Item .\start.* -Force
 +$csharpCode = @"
 +using System;
 +using System.Diagnostics;
 + 
 +namespace WindowsFormsApplication1
 +{
 +    static class Program
 +    {
 +        [STAThread]
 +        static void Main()
 +        {
 +            var filename = "PowerShell.exe";
 +            var parameter = "-NoExit -Command \"& {Write-Host -ForegroundColor Yellow -Object 'Hallo Kööln!'}\"";
 +            Process.Start(filename, parameter);
 +        }
 +    }
 +}
 +"@
 +Set-Content start.cs -Force -Value $csharpCode
 +C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:exe /out:start.exe start.cs #https://msdn.microsoft.com/en-us/library/78f4aasd.aspx
 +.\start.exe # Testen...
 +
 +#endregion
 +#region Beep StarWars
 +
 +[console]::beep(440,500) 
 +[console]::beep(440,500) 
 +[console]::beep(440,500) 
 +[console]::beep(349,350) 
 +[console]::beep(523,150) 
 +[console]::beep(440,500) 
 +[console]::beep(349,350) 
 +[console]::beep(523,150) 
 +[console]::beep(440,1000)
 +[console]::beep(659,500) 
 +[console]::beep(659,500) 
 +[console]::beep(659,500) 
 +[console]::beep(698,350) 
 +[console]::beep(523,150) 
 +[console]::beep(415,500) 
 +[console]::beep(349,350) 
 +[console]::beep(523,150) 
 +[console]::beep(440,1000)
 +
 +#endregion
 +#region Die Anzahl von Gruppen-Elementen bestimmen
 +
 +Get-ChildItem -Path C:\Windows\System32 -File -Force | Group-Object -Property Extension -NoElement | ? Count -gt 1 | Sort-Object -Property Count -Descending 
 +
 +#endregion
 +#region Eine Anzahl an Sekunden warten
 +
 +Start-Sleep -Seconds 3
 +
 +#endregion
 +#region Eine eindeutige Id erstellen
 +
 +# z.B. für Dateinamen, Primärschlüssel, ...
 +
 +New-Guid | select -ExpandProperty Guid
 +
 +#endregion
 +#endregion
 +#region CmdLet Beispiele
 +#region CmdLet: Get-EuroExchangeRate
 +
 +function Get-EuroExchangeRate
 +{
 +    <#
 +        .DESCRIPTION
 +            Zeigt und rechnet Euros in Nicht-Euro-Währung um.
 +         
 +        .PARAMETER Currency
 +            Die Ziel-Währung in die Euro's umgerechnet werden soll
 + 
 +        .PARAMETER Value
 +            Der umzurechnende Euro-Betrag.
 + 
 +        .PARAMETER ListCurrencys
 +            Listet alle möglichen Nicht-Euro-Währungen auf.
 + 
 +        .EXAMPLE
 +            Get-EuroExchangeRate -Currency usd
 +         
 +        .EXAMPLE
 +            "USD", "CAD" | Get-EuroExchangeRate -Value 100
 +         
 +        .EXAMPLE
 +            "USD", "CAD" | Get-EuroExchangeRate -Value 100 | ConvertTo-Html
 +         
 +        .EXAMPLE
 +            Get-EuroExchangeRate -ListCurrencys
 +         
 +        .NOTES
 +            Folgende Aufrufe dürfen nicht laufen:
 +             
 +            Get-EuroExchangeRate -ListCurrencys -Currency usd
 +    #>
 +    [CmdletBinding()]
 +    Param
 +    ([Parameter(Mandatory=$true, 
 +                 ValueFromPipeLine=$True, 
 +                 Position=0, 
 +                 ParameterSetName="rechnen")]
 +      [string]$Currency,
 +      [Parameter(Position=1, 
 +                 ParameterSetName="rechnen")]
 +      [PSDefaultValue(Help = '1')]
 +      [int]$Value = 1,
 +
 +      [Parameter(Position=0, 
 +                 ParameterSetName="anzeige")]
 +      [Switch]
 +      [bool]$ListCurrencys = $false
 +    )
 +    Begin
 +    {
 +        [xml]$doc = New-Object Xml
 +        $doc.Load("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml")
 +        $cubes = $doc.Envelope.Cube.Cube.Cube
 +
 +        if($ListCurrencys)
 +        {
 +            return new-object PSObject -Property @{"Currency" = ($cubes).currency}
 +        }
 +    }
 +    Process
 +    {
 +        if($ListCurrencys)
 +        {
 +            return
 +        }
 +
 +        if($cubes.currency -contains $Currency)
 +        {
 +            [decimal]$rate = ($cubes | where currency -eq $Currency).rate
 +            
 +            return new-object PSObject -Property @{
 +                "Currency" = $Currency.ToUpper(); 
 +                "EUR" = $Value;
 +                "ExchangeRate" = $rate * $Value}
 +        }
 +        else
 +        {
 +            Write-Warning -Message "Das Währungssymbol '$Currency' wurde nicht gefunden!"
 +        }
 +    }
 +}
 +
 +#endregion
 +#region CmdLet: Get-News
 +
 +function Get-News
 +{
 +    <#
 +        .Synopsis
 +           Zeigt RSS-Feeds an.
 +     
 +        .DESCRIPTION
 +           Zeigt ATOM Version 1.0 RRS-Feeds aus dem Internet oder Dateisystem an.
 +     
 +        .EXAMPLE
 +            Get-News -Uri http://rss.golem.de/rss.php?feed=ATOM1.0
 + 
 +            Liefert alle RSS-News-Feed von Golem.de.
 + 
 +        .EXAMPLE
 +            Get-News -Uri http://rss.golem.de/rss.php?feed=ATOM1.0 -First 3
 +         
 +        .EXAMPLE
 +            "http://www.heise.de/newsticker/heise-top-atom.xml", "http://rss.golem.de/rss.php?feed=ATOM1.0", "http://rss.golem.de/rss.php?tp=wirtschaft&feed=ATOM1.0" | Get-News -First 3
 +         
 +        .EXAMPLE
 +            Get-News -Uri "http://rss.golem.de/rss.php?feed=ATOM1.0" | ft
 +         
 +        .EXAMPLE
 +            Get-News -Uri "http://rss.golem.de/rss.php?feed=ATOM1.0" | where Titel -Like "E*" | ft
 +         
 +        .EXAMPLE
 +            Get-News -Uri "http://rss.golem.de/rss.php?feed=ATOM1.0" | Out-GridView
 +         
 +        .EXAMPLE
 +            Get-News -Uri "http://rss.golem.de/rss.php?feed=ATOM1.0" -First 2 -OpenLinkInBrowser
 +    #>
 +    Param
 +    (
 +        [Parameter(
 +            Mandatory         = $true,
 +            ValueFromPipeLine = $true)]
 +        [string]
 +        $Uri,
 +        
 +        [uint32]
 +        $First = [System.UInt32]::MaxValue,
 +
 +        [switch]
 +        [bool]
 +        $OpenLinkInBrowser = $false
 +    )
 +
 +    Begin # Zum Beginn 1x
 +    {
 +        $enUS        = New-Object -TypeName System.Globalization.CultureInfo -ArgumentList "en-US"
 +        $xmlDokument = New-Object -TypeName System.Xml.XmlDocument
 +    }
 +    Process # Je Objekt was über die Pip kommt
 +    {
 +        $FirstTemp = $First
 +        $xmlDokument.Load($Uri)
 +        foreach ($item in $xmlDokument.feed.entry)
 +        {
 +            if ($FirstTemp -gt 0)
 +            {
 +                $FirstTemp--
 +                $link = $item.link.href
 +                if($OpenLinkInBrowser)
 +                {
 +                    [System.Diagnostics.Process]::Start($link)
 +                    continue
 +                }
 +
 +                $title     = $item.title.InnerText
 +                $published = [datetime]::Parse($item.published, $enUS)
 +                $author    = $item.author.name
 +                $summery   = $item.summary.InnerText
 +                
 +                $result = @{"Titel"=$title; "StandUTC"=$published; "Autor"=$author; "Link"=$link; "Beschreibung"=$summery}
 +                New-Object psobject -Property $result | Select-Object Titel, StandUTC, Autor, Link, Beschreibung
 +            }
 +            else
 +            {
 +                break
 +            }   
 +        }
 +    }
 +    End # Zum Ende 1x
 +    {
 +    }
 +}
 +
 +#endregion
 +#endregion
 +#region Diagnose & Debugging
 +
 +#region Die Dauer einer Ausführung messen
 +
 +Measure-Command -Expression {gci C:\Users -File -Force -Recurse -ea SilentlyContinue}
 +
 +#endregion
 +
 +#endregion
 +#region Windows PowerShell ISE
 +#region Eigene Snippets für die ISE erstellen (STRG + J)
 +
 +New-Item -Path "$env:USERPROFILE\Documents\WindowsPowerShell\Snippets\" -Force -ItemType Directory
 +Set-Content -Path "$env:USERPROFILE\Documents\WindowsPowerShell\Snippets\Comment-BasedHelp.snippets.ps1xml" -Force -Value @"
 +<?xml version='1.0' encoding='utf-8' ?>
 +<Snippets xmlns='http://schemas.microsoft.com/PowerShell/Snippets'>
 +    <Snippet Version='1.0.0'>
 +        <Header>
 +            <Title>Comment-BasedHelp</Title>
 +            <Description>A template for comment-based help.</Description>
 +            <Author />
 +            <SnippetTypes>
 +                <SnippetType>Expansion</SnippetType>
 +            </SnippetTypes>
 +        </Header>
 +        <Code>
 +            <Script Language='PowerShell' CaretOffset='0'><![CDATA[
 +    <#
 +        .SYNOPSIS
 +        .DESCRIPTION
 +        .PARAMETER <Parameter-Name>
 +        .INPUTS
 +        .OUTPUTS
 +        .EXAMPLE
 +        .LINK
 +    #>
 +            ]]></Script>
 +        </Code>
 +    </Snippet>
 +</Snippets>
 +"@
 +
 +# s. STRG + J
 +
 +#endregion
 +#endregion
 +#region Windows
 +
 +#region BitLocker-CmdLet's
 +
 +Get-Command -Module BitLocker
 +
 +#endregion
 +#region ADS
 +
 +    # Group Policy Cmdlets in Windows PowerShell
 +    # https://technet.microsoft.com/en-us/library/ee461027.aspx
 +
 +    # Managing Group Policy with PowerShell
 +    # http://www.powershellmagazine.com/2012/05/14/managing-group-policy-with-powershell/
 +
 +    # Use PowerShell to Import Group Policy Objects
 +    # http://blogs.technet.com/b/heyscriptingguy/archive/2014/01/05/use-powershell-to-import-group-policy-objects.aspx
 +
 +    # ADSI Scripting with Windows PowerShell
 +    # http://blogs.msdn.com/b/arulk/archive/2006/07/25/678137.aspx
 +    # https://technet.microsoft.com/en-us/magazine/2007.06.powershell.aspx
 +    $objOU = [ADSI]"LDAP://localhost:389/ou=HR,dc=NA,dc=fabrikam,dc=com"    
 +    $objGroup = $objOU.Create("group", "cn=Atl-Users")
 +    $objGroup.Put("sAMAccountName", "Atl-Users"            # Festlegenn (put/get)
 +    $objGroup.SetInfo()                                      # Übernehmen
 +
 +    # Active Directory Recycle Bin Step-by-Step Guide
 +    # https://technet.microsoft.com/en-us/library/dd392261(v=ws.10).aspx
 +
 +    # PowerShell and Active Directory Recycle Bin
 +    # http://blogs.technet.com/b/heyscriptingguy/archive/2014/09/29/powershell-and-active-directory-recycle-bin.aspx
 +    # http://blogs.msdn.com/b/dsadsi/archive/2009/08/26/restoring-object-from-the-active-directory-recycle-bin-using-ad-powershell.aspx
 +    $oDomain = Get-ADDomain
 +    $DeletedObjects = $oDomain.DeletedObjectsContainer
 +    Restore-ADObject
 +
 +    #
 +    # ActiveDirectory-Modul
 +    #
 +
 +    Get-Module -ListAvailable ActiveDirectory
 +    Start-Process 'https://technet.microsoft.com/de-de/library/dd378937(v=ws.10).aspx' #Active Directory Administration with Windows PowerShell
 +
 +    # z.B.:
 +
 +    Get-ADUser -Filter "Surname -like 'Ber*'"
 +    Get-ADUser -Filter "Surname -like 'Ber*'" -SearchBase "OU=IT,DC=contoso,DC=com"
 +    Get-ADUser -Filter "Surname -like 'Ber*'" -Properties *
 +    Get-ADUser -Filter "StreetAddress -eq 'Rosenweg 1'"
 +    Search-ADAccount -PasswordNeverExpires -UsersOnly
 +    Get-ADUser -Filter "StreetAddress -eq 'Marsstr. 3'"|Set-ADUser -StreetAddress "Rosenweg 1"
 +    Get-ADUser -Filter * -SearchBase "OU=Marketing,DC=contoso,DC=com" | Set-ADUser -Manager PHuber
 +    Get-ADComputer -Filter "Name -like 'Win81*'"
 +
 +    Get-ADDomainController
 +
 +#endregion
 +#region Ein MSI-Paket installieren
 +
 +$msi = "c:\7z920-x64.msi"
 +$produkte = Get-WmiObject -Class Win32_Product -List
 +"Installation läuft ...."
 +$ergebnis = $produkte.Install($msi)
 +"... Installation fertig mit dem Ergebnis {0}" -f $ergebnis.ReturnValue
 +Get-WmiObject -Class Win32_Product | where Name -like "*7-zip*" | Get-Member
 +# ReturnValue siehe http://msdn.microsoft.com/en-us/library/aa390890(v=vs.85).aspx
 +
 +#endregion
 +#region CmdLet: Get-Product
 +
 +function Get-Product
 +{
 +    <#
 +    .Synopsis
 +       Kurzbeschreibung
 +    .DESCRIPTION
 +       Lange Beschreibung
 +    .EXAMPLE
 +       Beispiel für die Verwendung dieses Cmdlets
 +    .EXAMPLE
 +       Ein weiteres Beispiel für die Verwendung dieses Cmdlets
 +    #>
 +    [CmdletBinding()]
 +    [OutputType([int])]
 +    Param
 +    (
 +        # Hilfebeschreibung zu Param1
 +        [Parameter(ValueFromPipeline=$true)]
 +        [string[]]$ComputerName = "."
 +    )
 +
 +    Begin
 +    {
 +        [datetime]$heute = Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0
 +        Write-Debug -Message ("HEUTE: {0}" -f $heute)
 +    }
 +    Process
 +    {
 +        $produkte = Get-WmiObject -ComputerName $ComputerName -Class Win32_Product | 
 +            Sort-Object -Property Name
 +        
 +        foreach ($item in $produkte)
 +        {
 +            if($item.Name -eq $null) {continue}
 +
 +            $installDate = Get-Date -Year  $item.InstallDate.Substring(0, 4) `
 +                                    -Month $item.InstallDate.Substring(4, 2) `
 +                                    -Day   $item.InstallDate.Substring(6, 2) `
 +                                    -Hour 0 -Minute 0 -Second 0 -Millisecond 0
 +            
 +            $resultset = [ordered]@{
 +                "Name"          = $item.Name.Trim();
 +                "Version"       = $item.Version;
 +                "InstallDate"   = $installDate;
 +                "DurationInDays"= ($heute - $installDate).TotalDays;
 +                "ComputerName"  = $item.PSComputerName;}
 +            
 +            New-Object -TypeName psobject -Property $resultset
 +        }
 +    }
 +}
 +
 +Get-Product -Debug | ft
 +#Get-Product -ComputerName 192.168.50.50 | ft
 +#Get-Product -ComputerName 192.168.50.50, "."
 +#".", 192.168.50.50 | Get-Product
 +#Get-Product | Out-GridView
 +#Get-Product | where Name -Like *office*
 +#Get-Product | Out-File C:\Temp\produkte.txt
 +#Get-Help Get-Product -ShowWindow
 +
 +#endregion
 +#region Control-Panel's anzeigen
 +
 +Get-ControlPanelItem | sort Name
 +Get-ControlPanelItem -Name "Windows To Go" | Show-ControlPanelItem
 +
 +#endregion
 +
 +#endregion
 +#region Office
 +
 +#region Auf Outlook zugreifen
 +# Outlook muss installiert sein
 +# zzgl. Office PIA bis Office 2010 AB in Office-Installation
 +# ALTERNATIVE: Exchange PowerShell-Modul installieren
 +$outlook = new-object -ComObject Outlook.Application 
 +$namespace = $outlook.GetNamespace("MAPI")
 +$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
 +Write-Host "ORDNER:" $inbox.Name
 +foreach($item in $inbox.Items)
 +{
 +    Write-Host " SUBJECT:" $item.Subject
 +}
 +#endregion
 +
 +#endregion
 +#region Netzwerk
 +
 +#region Netzwerk-Freigabe erstellen
 +
 +New-SmbShare -Name Data -Path C:\Windows
 +Remove-SmbShare -Name Data -Confirm:$false
 +
 +#endregion
 +#region Netzwerk-Adapter neustarten
 +
 +Get-NetAdapter -IncludeHidden                       # Übersicht
 +Restart-NetAdapter -Name WLAN                       # Einen
 +Restart-NetAdapter -Name * -PassThru -IncludeHidden # Alle
 +
 +#endregion
 +
 +#endregion
 +#region IO, FileSystem, OneDrive, Database, Web
 +
 +#region Eine temporäre Datei erstellen
 +
 +$temp = New-TemporaryFile
 +$temp
 +
 +#endregion
 +#region Alle temporären Dateien löschen
 +
 +$path = Join-Path -Path $env:USERPROFILE -ChildPath "AppData\Local\Temp\"
 +Remove-Item -Path $path -Recurse -Force -ea SilentlyContinue
 +
 +#endregion
 +#region Hash einer Datei berechnen
 +
 +# ... um identische Dateien zu finden, oder der Unversehrtheit z.B. beim übertragen zu gewehrleisten
 +
 +Get-FileHash -Path C:\Windows\explorer.exe
 +
 +#endregion
 +#region Dateien in ein Archiv kompremieren
 +
 +Get-ChildItem -Path C:\windows\Logs -File -Force -Recurse -ea SilentlyContinue | 
 +    ? Extension -EQ ".log"
 +    Compress-Archive -DestinationPath C:\temp\logs.zip -CompressionLevel Optimal -ErrorAction SilentlyContinue
 +
 +#endregion
 +#region Den tatsächlien Ort von Spezial-Ordner lokalisieren
 +
 +[Environment]::GetFolderPath([System.Environment+SpecialFolder]::DesktopDirectory); # RICHTIG
 +"$env:USERPROFILE\Desktop"                                                          # falsch
 +
 +#endregion
 +#region .NET: Datei beschreiben
 +
 +$datei = New-Item -Path C:\temp\meinlog3.txt -ItemType File -Force
 +$writer = $datei.CreateText()   # Für Byte-Daten .OpenWrite()
 +for ($i = 1; $i -lt 99; $i++)
 +
 +    $logText = ("{0:yyyyMMddTHHmmss.ff}: Meldung Nr. {1}" -f (Get-Date), $i)
 +    $writer.WriteLine($logText)
 +}
 +$writer.Close()
 +Get-Content -Path C:\temp\meinlog3.txt | Out-GridView
 +
 +#endregion
 +#region .NET: Datei lesen
 +
 +$datei = Get-Item -Path C:\Windows\Logs\DirectX.log # | Get-Member
 +$reader = $datei.OpenText() # | Get-Member # Siehe auch .OpenRead() für Byte-Datei
 +
 +while (-not $reader.EndOfStream)
 +{
 +    $zeile = $reader.ReadLine()
 +    $zeile
 +}
 +
 +$reader.Close()
 +
 +#endregion
 +#region CmdLet: Get-BigFile
 +
 +function Get-BigFile
 +{
 +    <#
 +        .SYNOPSIS
 +            Zeigt eine Übersicht von großen Dateien und deren Besitzer.
 +         
 +        .EXAMPLE
 +            Get-BigFile -Path C:\ -GE 100MB
 +         
 +        .EXAMPLE
 +            "C:\Users", "C:\Program Files" | Get-BigFile -GE 100MB
 +    #>
 +    [CmdletBinding( HelpUri = 'http://www.gfu.net/')]
 +    [Alias("gbf")]
 +    [OutputType([PSCustomObject])]
 +    Param
 +    (
 +        # Pfad an dem große Dateien gefunden werden sollen
 +        [Parameter(ValueFromPipeLine = $true)]
 +        [string]
 +        $Path = ".",
 +
 +        # Eine Datei ist Groß wenn sie größer gleich diesem Wert ist
 +        [Parameter(Mandatory = $true)]
 +        [uint32]
 +        $GE = 10MB,
 +        
 +        [switch]
 +        [bool]
 +        $Recurse = $false
 +    )
 +
 +    Process
 +    {
 +        Get-ChildItem -Path $Path -Recurse:$Recurse -File -Force -ea SilentlyContinue | 
 +            Where-Object -Property Length -GE -Value $GE | 
 +            ForEach-Object -Process {
 +                [PSCustomObject]@{ 
 +                    Name           = $_.Name;
 +                    Length         = $_.Length;
 +                    Owner          = $_ | Get-Acl -ea SilentlyContinue | select -exp Owner;
 +                    LastAccessTime = $_.LastAccessTime;
 +                    FullName       = $_.FullName;
 +                }
 +            }
 +    }
 +}
 +New-Alias -Name gbf -Value Get-BigFile -Force
 +
 +#Get-Help Get-BigFile -ShowWindow
 +#Show-Command Get-BigFile
 +#Get-BigFile -Path c:\ -GE 100MB -Recurse
 +#Get-BigFile -GE 100MB
 +#"C:\Users", "C:\Program Files" | Get-BigFile -GE 100MB -Recurse | Out-GridView
 +
 +#endregion
 +#region CmdLet: Get-OldFile
 +
 +function Get-OldFile
 +{
 +    <#
 +        .SYNOPSIS
 +            Ermittelt alte Dateien.
 +                          
 +        .DESCRIPTION
 +            Ermittelt alte Dateien nach Anzahl Tage.
 + 
 +        .INPUTS
 + 
 +        .OUTPUTS
 +            Liefert ein ein PSObject mit folgenden Eigenschaften.....
 + 
 +        .EXAMPLE
 +            Get-OldFile -Path C:\Windows -OlderThanDays 180
 +            Alle alte Datei aus c:\windows die älter sind als 180 Tage.
 + 
 +        .EXAMPLE
 +            Get-OldFile -Path C:\Windows -OlderThanDays 180 -Recurse
 +            Alle alte Datei aus c:\windows und Unterordner die älter sind als 180 Tage.
 +    #>
 +    param(
 +        [Parameter(ParameterSetName  = "Days", 
 +                   ValueFromPipeline = $true)]
 +        [Parameter(ParameterSetName  = "DateTime", 
 +                   ValueFromPipeline = $true)]
 +        [ValidateScript({Test-Path $_})]
 +        [string]$Path = ".", 
 +        
 +        # Gültige Werte für Tag sind 1 bis 4000
 +        [Parameter(Mandatory        = $true, 
 +                   ParameterSetName = "Days")]
 +        [ValidateRange(1, 4000)]
 +        [UInt32]$OlderThanDays, 
 +        
 +        [Parameter(ParameterSetName = "Days")]
 +        [Parameter(ParameterSetName = "DateTime")]
 +        [switch]
 +        [bool]$Recurse,
 +
 +        [Parameter(Mandatory        = $true, 
 +                   ParameterSetName = "DateTime")]
 +        [ValidateScript({$_ -le (Get-Date)})]
 +        [datetime]$OlderThan
 +    )
 +
 +    begin { # Inizialisierungs-Code
 +        $lastAccessTime = $null
 +        switch ($psCmdLet.ParameterSetName)
 +        {
 +            'Days'     {$lastAccessTime = (Get-Date).AddDays($OlderThanDays * -1)}
 +            'DateTime' {$lastAccessTime = $OlderThan}
 +        }
 +    }
 +    
 +    process
 +    {
 +        Get-ChildItem -Path $Path -Recurse:$Recurse -File -ErrorAction SilentlyContinue | 
 +            Where-Object -Property LastAccessTime -lt $lastAccessTime | 
 +            ForEach-Object -Process {
 +            $now = Get-Date
 +            $owner = $_ | Get-Acl | Select-Object -ExpandProperty Owner
 +            $result = [ordered]@{
 +                Name           = $_.Name; 
 +                AgeInDays      = [Math]::Round(($now - $_.LastAccessTime).TotalDays, 2); 
 +                Owner          = $owner;
 +                LastAccessTime = $_.LastAccessTime; 
 +                FullName       = $_.FullName}
 +            New-Object PSObject -Property $result
 +        }
 +    }
 +}
 +#Get-Help Get-OldFile -ShowWindow
 +#Show-Command Get-OldFile
 +Get-OldFile -Path c:\ -Recurse -OlderThanDays 1200 | Out-GridView
 +Get-OldFile -Path c:\ -Recurse -OlderThan "2013-12-31" | Out-GridView
 +
 +#
 +# KOMPONENTENTEST
 +#
 +
 +#Get-OldFiles -Path C:\Windows -OlderThanDays 180 -Recurse
 +#Get-OldFiles -Path C:\Windows -OlderThanDays 180 | Out-GridView
 +#Get-OldFiles -OlderThanDays 180 -Path C:\Windows -Recurse
 +#Get-help Get-OldFiles -ShowWindow
 +#Get-OldFiles -Path C:\Windows -OlderThanDays 180 -OlderThan (Get-Date).AddMonths(-50)
 +#Get-OldFiles -Path C:\Windows -OlderThanDays 180
 +#Get-OldFiles -Path C:\Windows -OlderThan (Get-Date)
 +#Get-Help Where-Object -Online
 +#"c:\windows", "C:\windows\System32" | Get-OldFiles -OlderThanDays 180
 +
 +#endregion
 +#region CmdLet: Get-XFile (Kombination aus Get-OldFile und Get-BigFile)
 +
 +function Get-XFile
 +{
 +    <#
 +        .SYNOPSIS
 +            Ermittelt alte und oder große Dateien.
 +    #>
 +    param(
 +        [Parameter(ParameterSetName  = "Days", 
 +                   ValueFromPipeline = $true)]
 +        [Parameter(ParameterSetName  = "DateTime", 
 +                   ValueFromPipeline = $true)]
 +        [ValidateScript({Test-Path $_})]
 +        [string]$Path, 
 +        
 +        # Gültige Werte für Tag sind 1 bis 4000
 +        [Parameter(ParameterSetName = "Days")]
 +        [ValidateRange(1, 4000)]
 +        [UInt32]$OlderThanDays, 
 +
 +        [Parameter(ParameterSetName = "DateTime")]
 +        [ValidateScript({$_ -le (Get-Date)})]
 +        [datetime]$OlderThan,
 +
 +
 +        # Eine Datei ist Groß wenn sie größer gleich diesem Wert ist
 +        [Parameter(ParameterSetName = "Days")]
 +        [Parameter(ParameterSetName = "DateTime")]
 +        [uint32]
 +        $LengthGE = 0,
 +
 +        [Parameter(ParameterSetName = "Days")]
 +        [Parameter(ParameterSetName = "DateTime")]
 +        [switch]
 +        [bool]$AgeOrBig,
 +
 +        [Parameter(ParameterSetName = "Days")]
 +        [Parameter(ParameterSetName = "DateTime")]
 +        [switch]
 +        [bool]$Recurse
 +    )
 +
 +    begin { # Inizialisierungs-Code
 +        $lastAccessTime = $null
 +        switch ($psCmdLet.ParameterSetName)
 +        {
 +            'Days'     {$lastAccessTime = (Get-Date).AddDays($OlderThanDays * -1)}
 +            'DateTime' {$lastAccessTime = $OlderThan}
 +        }
 +        $filterScript = $null
 +        if($AgeOrBig)
 +        {
 +            $filterScript = {$_.LastAccessTime -lt $lastAccessTime -or  $_.Length -ge $LengthGE }
 +        }
 +        else
 +        {
 +            $filterScript = {$_.LastAccessTime -lt $lastAccessTime -and $_.Length -ge $LengthGE }
 +        }
 +    }
 +    
 +    process
 +    {
 +        Get-ChildItem -Path $Path -Recurse:$Recurse -File -ErrorAction SilentlyContinue | 
 +            Where-Object -FilterScript $filterScript |  
 +            ForEach-Object -Process {
 +                $now = Get-Date
 +                $owner = $_ | Get-Acl | Select-Object -ExpandProperty Owner
 +                $result = [ordered]@{
 +                    Name           = $_.Name; 
 +                    Length         = $_.Length; 
 +                    AgeInDays      = [Math]::Round(($now - $_.LastAccessTime).TotalDays, 2); 
 +                    Owner          = $owner;
 +                    LastAccessTime = $_.LastAccessTime; 
 +                    FullName       = $_.FullName}
 +                New-Object PSObject -Property $result
 +        }
 +    }
 +}
 +
 +Get-Help -Name Get-XFile -ShowWindow
 +Show-Command -Name Get-XFile
 +Get-XFile -Path c:\ -Recurse -OlderThanDays 300                          | Out-GridView
 +Get-XFile -Path c:\ -Recurse                    -LengthGE 10MB           | Out-GridView
 +Get-XFile -Path c:\ -Recurse -OlderThanDays 300 -LengthGE 10MB           | Out-GridView
 +Get-XFile -Path c:\ -Recurse -OlderThanDays 300 -LengthGE 10MB -AgeOrBig | Out-GridView
 +
 +#endregion
 +#region CmdLet: Test-ReadAccess
 +
 +function Test-ReadAccess
 +{
 +    <# #about_Comment_Based_Help
 +        .DESCRIPTION
 +            Prüft ob Lese-Rechte vorliegen
 +         
 +        .EXAMPLE
 +            Test-ReadAccess c:\test.html
 +         
 +        .EXAMPLE
 +            Test-ReadAccess c:\geheim.txt
 +         
 +        .EXAMPLE
 +            "c:\test.html" | Test-ReadAccess
 +         
 +        .EXAMPLE
 +            "c:\geheim.txt" | Test-ReadAccess
 +         
 +        .EXAMPLE
 +            ls c:\*.* -Force | Test-ReadAccess
 + 
 +        .INPUTS
 +            Hinweise zur Eingabe
 + 
 +        .OUTPUTS
 +            Hinweise zur Ausgabe
 + 
 + 
 +        .NOTES
 +            Siehe auch Get-Acl
 +    #>
 +    [CmdletBinding()]
 +    Param
 +    ([Parameter(Mandatory=$true, 
 +                 ValueFromPipeLine=$True)]
 +      [string]$Path
 +    )
 +    Process
 +    {
 +        try
 +        {
 +            Get-Content $Path -ErrorAction Stop -ReadCount 1 | Out-Null
 +            
 +            return new-object PSObject -Property @{
 +                "Path" = $Path; 
 +                "IsReadAccess" = $true}
 +        }
 +        catch [UnauthorizedAccessException]
 +        {
 +            return new-object PSObject -Property @{
 +                "Path" = $Path; 
 +                "IsReadAccess" = $false}
 +        }
 +    }
 +}
 +
 +#endregion
 +#region .NET: Datenbank zugriff (ADO.NET)
 +
 +#
 +# 1. Connection-Objekt erstellen
 +#
 +
 +# Microsoft SQL Server => System.Data.SqlClient
 +# Diverse DBMS => System.Data.Odbc ODER System.Data.OleDb
 +# MySql => Siehe Hersteller bzgl. .NET Treiber
 +# PostgreSQL => Siehe Hersteller bzgl. .NET Treiber
 +# Orcale => Siehe Hersteller bzgl. .NET Treiber
 +
 +#
 +# Connection-String siehe http://www.connectionstrings.com
 +#
 +
 +$conn = New-Object -TypeName System.Data.SqlClient.SqlConnection  
 +$conn.ConnectionString = "Server=.; Database=AdventureWorks2014; Trusted_Connection=True;"
 +$conn.Open()
 +#$ta = $conn.BeginTransaction()
 +
 +#
 +# 2. Command-Objekt erstellen
 +#
 +
 +$cmd = New-Object -TypeName System.Data.SqlClient.SqlCommand
 +$cmd.Connection = $conn
 +$cmd.CommandText = "SELECT * FROM Person.Person"
 +
 +#
 +# 3.1 Nutzen (z.B. Reader-Objekt)
 +#
 +
 +$reader = $cmd.ExecuteReader()
 +while ($reader.Read())
 +{
 +    "VN: {0} NN: {1} BusinessEntityID {2}" -f $reader["FirstName"], $reader["LastName"], $reader["BusinessEntityID"]
 +}
 +
 +#$ta.Commit()
 +$conn.Close()
 +
 +#
 +# 3.2 Nutzen (z.B. Datensatz ändern)
 +#
 +
 +$conn.Open()
 +$cmd.CommandText = "UPDATE Person.Person SET LastName='MÜLLLER' WHERE BusinessEntityID='hmmmmmm'"
 +$ergebnis = $cmd.ExecuteNonQuery()
 +"Betroffene Zeilen: {0}" -f $ergebnis
 +$conn.Close()
 +
 +#
 +# 3.3 Nutzen (z.B. analysieren)
 +#
 +
 +$conn.Open()
 +$cmd.CommandText = "SELECT COUNT(*) FROM Person.Person"
 +$ergebnis = $cmd.ExecuteScalar()
 +"In der Tabelle Person.Person sind {0} Datensätze enthalten" -f $ergebnis
 +$conn.Close()
 +
 +#endregion
 +#region .NET: Dateiauswahl (WinForms.CommonDialogs)
 +   
 +$Dlg = New-Object System.Windows.Forms.OpenFileDialog
 +$Dlg.Filter = "PowerShell-Skripten (*.ps1)|*.ps1|Text-Dateien (*.txt)|*.txt|Alle Dateien (*.*)|*.*"
 +$Dlg.InitialDirectory = "C:\Temp"
 +$DialogResult = $Dlg.ShowDialog()
 +if ($DialogResult -eq [System.Windows.Forms.DialogResult]::OK)
 +{
 +    $Dlg.Filename
 +}
 +
 +$Dlg = New-Object System.Windows.Forms.FolderBrowserDialog
 +$Dlg.Description = "Bitte Ordner für das Archivieren auswählen"
 +$Dlg.RootFolder = [System.Environment+SpecialFolder]::Desktop
 +$Dlg.ShowNewFolderButton = $true
 +$DialogResult = $Dlg.ShowDialog()
 +if ($DialogResult -eq [System.Windows.Forms.DialogResult]::OK)
 +{
 +    $Dlg.SelectedPath
 +}
 +
 +# SaveAsDialog, ColorPicker, FontPicker
 +
 +#endregion
 +#region HTTP-Kommunikation (Web-Request)
 +
 +$webrequest = Invoke-WebRequest -Uri "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
 +$xml = [xml]$webrequest.Content
 +$xml.Envelope.Cube.Cube.Cube | ? currency -eq "USD"
 +
 +#endregion
 +#region Proxy-Server
 +
 +$webProxy = New-Object System.Net.WebProxy("proxy.gewkoelnag.de:8080", $true)
 +$webProxy.Credentials = New-Object Net.NetworkCredential("user","password","domain.local")
 +$netCred = New-Object System.Net.NetworkCredentials("user", "pwd", "domain");   
 +$webClient = New-Object System.Net.WebClient
 +#$webClient.Proxy = $webProxy
 +$webClient.Credentials=$netCred
 +$page = $webClient.DownloadString("http://intranet/content/de/index.php")
 +#$webClient.Upload.....
 +
 +#endregion
 +#region Bing-Images downloaden
 +
 +$ImageUrl = "http://www.bing.com/HPImageArchive.aspx?format=xml&idx=1&n=8&mkt=de-DE"; 
 +$response = Invoke-WebRequest -Uri $ImageUrl; 
 +$xmlDocument = [xml]$response.Content
 +$xmlDocument.images.image | % {
 +    $uri = "http://www.bing.com$($_.url -replace "_1366x768.jpg", "_1920x1080.jpg")"
 +    $filename = "C:\temp\BingImage $($_.startdate) $($_.copyright.Replace('/', '; ')).jpg"
 +    Invoke-WebRequest -Uri $uri -OutFile $filename
 +}
 +
 +#endregion
 +#region Zugriff auf OneDrive
 +
 +Install-Module -Name OneDrive -Scope CurrentUser -AllowClobber -Force
 +
 +Start-Process https://apps.dev.microsoft.com/?referrer=https%3A%2F%2Fdev.onedrive.com#/appList
 +$auth = Get-ODAuthentication -ClientID "00000000....."
 +$accessToken = $auth.access_token
 +
 +# ODER
 +
 +Start-Process https://dev.onedrive.com/auth/msa_oauth.htm
 +$accessToken = "EwAoA61DBAAUGCCXc..."
 +
 +Add-ODItem  -LocalFile (Join-Path -Path $env:USERPROFILE -ChildPath "Desktop\PowerShell V.5.12.ps1") -Path "/Wissen/PowerShell" -AccessToken $accessToken
 +
 +#endregion
 +
 +#endregion
 +#region .NET Framework, CLI
 +#region using namespace-Anweisung (Require PS 5.0)
 +
 +using namespace System.Diagnostics;
 +
 +#endregion
 +#endregion
 +#region UserInterface, GUI
 +
 +#region Keyboard-Taste vom User abfragen
 +
 +$key = [console]::ReadKey($true)
 +$key.KeyChar
 +
 +#endregion
 +#region .NET: Dateiauswahl (WinForms.CommonDialogs)
 +   
 +$Dlg = New-Object System.Windows.Forms.OpenFileDialog
 +$Dlg.Filter = "PowerShell-Skripten (*.ps1)|*.ps1|Text-Dateien (*.txt)|*.txt|Alle Dateien (*.*)|*.*"
 +$Dlg.InitialDirectory = "C:\Temp"
 +$DialogResult = $Dlg.ShowDialog()
 +if ($DialogResult -eq [System.Windows.Forms.DialogResult]::OK)
 +{
 +    $Dlg.Filename
 +}
 +
 +$Dlg = New-Object System.Windows.Forms.FolderBrowserDialog
 +$Dlg.Description = "Bitte Ordner für das Archivieren auswählen"
 +$Dlg.RootFolder = [System.Environment+SpecialFolder]::Desktop
 +$Dlg.ShowNewFolderButton = $true
 +$DialogResult = $Dlg.ShowDialog()
 +if ($DialogResult -eq [System.Windows.Forms.DialogResult]::OK)
 +{
 +    $Dlg.SelectedPath
 +}
 +
 +# SaveAsDialog, ColorPicker, FontPicker
 +
 +#endregion
 +#region .NET: Eingabefenster/InputBox (WinForms)
 +
 +function inputbox([System.String]$strPrompt="", [System.String]$strTitel="", [System.String]$strWert="")
 +{
 +   $form=""
 +   $global:erg=$false
 +   $global:eingabe=""
 +   $farbe=0
 +   $pruefen={
 +      if ($eingabefeld.Text.Trim() -gt "")
 +      {
 +         $bttOK.Enabled=$true
 +      }
 +      else
 +      {
 +         $bttOK.Enabled=$false
 +      }    
 +   }
 +   $farbe=[System.Drawing.Color]::FromArgb(255, 0, 0)
 +   
 +   $form=New-Object "System.Windows.Forms.Form"
 +   $form.TopMost = $true
 +   $form.Text=$strTitel
 +
 +   $label=New-Object "System.Windows.Forms.Label"
 +   $label.Height=20
 +   $label.Text=$strPrompt
 +   $label.Top=10
 +   $label.Width=$form.width-10
 +   $label.Left=5
 +   $label.ForeColor=$farbe
 +   
 +   #Einabefeld erzeugen
 +   $eingabefeld=New-Object "System.Windows.Forms.Textbox"
 +   $eingabefeld.Height=20
 +   $eingabefeld.Text=$strWert
 +   $eingabefeld.Top=$label.Top +$label.Height+5
 +   $eingabefeld.Left=$label.Left
 +   $eingabefeld.TabIndex=0
 +   
 +   #Buttons erstellen
 +   $bttOK= New-Object "System.Windows.Forms.Button"
 +   $bttOK.Text = "OK"   
 +   $bttOK.Left=$label.Left
 +   $bttOK.Top=$eingabefeld.top + $eingabefeld.height + 10 
 +   $bttOK.Width=70
 +   $bttOK.TabIndex=1
 +   
 +   $bttAbbrechen= New-Object "System.Windows.Forms.Button"
 +   $bttAbbrechen.Text = "Abbrechen"
 +   $bttAbbrechen.Top=$bttOK.top
 +   $bttAbbrechen.Width=70
 +   $bttAbbrechen.Left=$bttOK.left + $bttOK.width + 10
 +   $bttAbbrechen.TabIndex=2
 +   $farbe=[System.Drawing.Color]::FromArgb(255, 128, 0)
 +   $bttOK.BackColor=$farbe
 +   $bttAbbrechen.BackColor=$farbe
 +   
 +   $form.Backcolor=[System.Drawing.Color]::FromArgb(255, 255, 0)
 +   $form.Controls.Add($label)
 +   $form.Controls.Add($bttOK)
 +   $form.Controls.Add($bttAbbrechen)
 +   $form.Controls.Add($eingabefeld)
 +   $form.Height=$eingabefeld.top + $eingabefeld.height + 10 + $bttOK.height + $bttOK.top
 +   
 +   #Eventhandler erstellen
 +   $bttAbbrechen.Add_Click({
 +       $global:erg=$false;
 +       $global:eingabe="";
 +       $form.Close();
 +       $form.Dispose();
 +   })
 +   
 +   $bttOK.Add_Click({
 +        $global:erg=$true;
 +        $global:eingabe=$eingabefeld.Text; 
 +        $form.Close();
 +        $form.Dispose()})    
 +   
 +   $form.Add_Load({&$pruefen})
 +   $eingabefeld.Add_TextChanged({&$pruefen})
 +   
 +   $temp=$form.ShowDialog()
 +   
 +   #Rueckgabewert pruefen
 +   if ($global:erg -eq $true)
 +   {
 +      return $global:eingabe    
 +   }
 +   else
 +   {
 +      return $false
 +   }
 +   
 +}
 +
 +#Skriptinhalt
 +#echo (getScriptName)
 +$Erg=[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
 +$Erg=[reflection.assembly]::LoadWithPartialName(
 +   "System.Drawing")
 +
 +echo (inputbox "Bitte Wert eingeben!" "Eingabe notwendig!" )
 +
 +#endregion
 +#region .NET: MessageBox (WinForms.CommonDialogs)
 +
 +#System.Windows.Forms.dll (Assembly aus dem GAC) laden
 +$erg=[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
 +
 +[System.Windows.Forms.MessageBox]::Show("Hallo Welt!")
 +
 +
 +if ([System.Windows.Forms.MessageBox]::Show(
 +        "Moechten Sie das Skript abbrechen?",
 +        ("PowerShell:" + $meinName), 
 +        [System.Windows.Forms.MessageBoxButtons]::YesNo,
 +        [System.Windows.Forms.MessageBoxIcon]::Question) `
 +    -eq [System.Windows.Forms.DialogResult]::Yes)
 +{
 +   echo "Abbruch durch den Benutzer ..."
 +   exit
 +}
 +else
 +{
 +   #hier folgt weiterer Code
 +   echo "Skript wird fortgesetzt ..."
 +}
 +#[System.Windows.Forms.MessageBox]::Show("Fehler!","",0,16)
 +#[System.Windows.Forms.MessageBox]::Show("Info!","",0,64)
 +#[System.Windows.Forms.MessageBox]::Show("Warnung!","",0,48)
 +
 +#endregion
 +#region .NET: 'Hallo Welt'-Fenster (WPF)
 +
 +function Start-WpfHalloWelt
 +{
 +    Add-Type -AssemblyName PresentationFramework
 +    Add-Type -AssemblyName System
 +
 +    [XML]$XAML = @"
 +    <Window x:Class="FSM.PS_WPF_Sample"
 +            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 +            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 +            Title="PS_WPF_Sample" Height="200" Width="300">
 +        <Grid>
 +            <TextBox x:Name="PSText" Text="Hello World" HorizontalAlignment="Left" Margin="65,40,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="175"/>
 +            <Label x:Name="PSLabel" Content="Type in the box and click OK" HorizontalAlignment="Left" Margin="65,80,0,0" VerticalAlignment="Top" Width="175" BorderThickness="1"/>
 +            <Button x:Name="PSBtnOK" Content="OK" HorizontalAlignment="Left" Margin="65,130,0,0" VerticalAlignment="Top" Width="75"/>
 +            <Button x:Name="PSBtnExit" Content="Exit" HorizontalAlignment="Left" Margin="165,130,0,0" VerticalAlignment="Top" Width="75"/>
 +        </Grid>
 +    </Window>
 +"@
 +
 +    $XAML.Window.RemoveAttribute("x:Class")
 +    $Reader = New-Object System.Xml.XmlNodeReader $XAML
 +    $Form = [Windows.Markup.XamlReader]::Load($Reader)
 +
 +    $PSText    = $Form.FindName('PSText')
 +    $PSLabel   = $Form.FindName('PSLabel')
 +    $PSBtnOK   = $Form.FindName('PSBtnOK')
 +    $PSBtnExit = $Form.FindName('PSBtnExit')
 +
 +    $btnOKClick   = {$PSLabel.Content = $PSText.Text}
 +    $btnExitClick = {$Form.Close()}
 +
 +    $PSBtnOK.Add_Click($btnOKClick)
 +    $PSBtnExit.Add_Click($btnExitClick)
 +
 +    $Form.ShowDialog()
 +}
 +
 +Start-WpfHalloWelt
 +
 +#endregion
 +#region .NET: Komplexe Fenster z.B. Uhr (WPF)
 +
 +function Start-WpfUhr
 +{
 +                                                                                                        <#
 +    .SYSNOPSIS
 +        Displays a clock on the screen with date.
 + 
 +    .DESCRIPTION
 +        Displays a clock on the screen with date.
 + 
 +    .PARAMETER TimeColor
 +        Specify the color of the time display.
 + 
 +    .PARAMETER DateColor
 +        Specify the color of the date display.
 + 
 +        Default is White
 + 
 +    .EXAMPLE
 +        .\ClockWidget.ps1
 + 
 +        Description
 +        -----------
 +        Clock is displayed on screen
 + 
 +    .EXAMPLE
 +        .\ClockWidget.ps1 -TimeColor DarkRed -DateColor Gold
 + 
 +        Description
 +        -----------
 +        Clock is displayed on screen with alternate colors
 + 
 +    .EXAMPLE
 +        .\ClockWidget.ps1 –TimeColor "#669999" –DateColor "#334C4C"
 + 
 +        Description
 +        -----------
 +        Clock is displayed on screen with alternate colors as hex values
 +             
 +    #>
 +                    Param (
 +    [parameter()]
 +    [string]$TimeColor = "White",
 +    [parameter()]
 +    [string]$DateColor = "White"
 +    )
 +    $Clockhash = [hashtable]::Synchronized(@{})
 +    $Runspacehash = [hashtable]::Synchronized(@{})
 +    $Runspacehash.host = $Host
 +    $Clockhash.TimeColor = $TimeColor
 +    $Clockhash.DateColor = $DateColor
 +    $Runspacehash.runspace = [RunspaceFactory]::CreateRunspace()
 +    $Runspacehash.runspace.ApartmentState = “STA”
 +    $Runspacehash.runspace.ThreadOptions = “ReuseThread”
 +    $Runspacehash.runspace.Open() 
 +    $Runspacehash.psCmd = {Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase}.GetPowerShell() 
 +    $Runspacehash.runspace.SessionStateProxy.SetVariable("Clockhash",$Clockhash)
 +    $Runspacehash.runspace.SessionStateProxy.SetVariable("Runspacehash",$Runspacehash)
 +    $Runspacehash.runspace.SessionStateProxy.SetVariable("TimeColor",$TimeColor)
 +    $Runspacehash.runspace.SessionStateProxy.SetVariable("DateColor",$DateColor)
 +    $Runspacehash.psCmd.Runspace = $Runspacehash.runspace 
 +    $Runspacehash.Handle = $Runspacehash.psCmd.AddScript({ 
 +
 +                                 $Script:Update = {
 +    $day,$Month, $Day_n, $Year, $Time, $AMPM = (Get-Date -f "dddd,MMMM,dd,yyyy,HH:mm,tt") -Split ','
 +
 +    $Clockhash.time_txtbox.text = $Time.TrimStart("0")
 +    $Clockhash.day_txtbx.Text = $day
 +    $Clockhash.ampm_txtbx.text = $AMPM
 +    $Clockhash.day_n_txtbx.text = $Day_n
 +    $Clockhash.month_txtbx.text = $Month
 +    $Clockhash.year_txtbx.text = $year   
 +    }
 +
 +                                                                                                                                                                                    [xml]$xaml = @"
 +<Window
 +        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 +        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 +        WindowStyle = "None" WindowStartupLocation = "CenterScreen" SizeToContent = "WidthAndHeight" ShowInTaskbar = "False"
 +        ResizeMode = "NoResize" Title = "Weather" AllowsTransparency = "True" Background = "Transparent" Opacity = "1" Topmost = "True">
 +    <Grid x:Name = "Grid" Background = "Transparent">
 +        <TextBlock x:Name = "time_txtbox" FontSize = "72" Foreground = "$($Clockhash.TimeColor)" VerticalAlignment="Top"
 +        HorizontalAlignment="Left" Margin="0,-26,0,0">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "5" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +        <TextBlock x:Name = "ampm_txtbx" FontSize= "20" Foreground = "$($Clockhash.TimeColor)" Margin = "133,0,0,0"
 +        HorizontalAlignment="Left">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "2" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +        <TextBlock x:Name = "day_n_txtbx" FontSize= "38" Foreground = "$($Clockhash.DateColor)" Margin="5,42,0,0"
 +        HorizontalAlignment="Left">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "2" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +        <TextBlock x:Name = "month_txtbx" FontSize= "20" Foreground = "$($Clockhash.DateColor)" Margin="54,48,0,0"
 +        HorizontalAlignment="Left">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "2" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +        <TextBlock x:Name = "day_txtbx" FontSize= "15" Foreground = "$($Clockhash.DateColor)" Margin="54,68,0,0"
 +        HorizontalAlignment="Left">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "2" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +        <TextBlock x:Name = "year_txtbx" FontSize= "38" Foreground = "$($Clockhash.DateColor)" Margin="0,42,0,0"
 +        HorizontalAlignment="Left">
 +                <TextBlock.Effect>
 +                    <DropShadowEffect Color = "Black" ShadowDepth = "1" BlurRadius = "2" />
 +                </TextBlock.Effect>
 +        </TextBlock>
 +    </Grid>
 +</Window>
 +"@
 + 
 +    $reader=(New-Object System.Xml.XmlNodeReader $xaml)
 +    $Clockhash.Window=[Windows.Markup.XamlReader]::Load( $reader )
 +
 +    $Clockhash.time_txtbox = $Clockhash.window.FindName("time_txtbox")
 +    $Clockhash.ampm_txtbx = $Clockhash.Window.FindName("ampm_txtbx")
 +    $Clockhash.day_n_txtbx = $Clockhash.Window.FindName("day_n_txtbx")
 +    $Clockhash.month_txtbx = $Clockhash.Window.FindName("month_txtbx")
 +    $Clockhash.year_txtbx = $Clockhash.Window.FindName("year_txtbx")
 +    $Clockhash.day_txtbx = $Clockhash.Window.FindName("day_txtbx")
 +
 +    #Timer Event
 +    $Clockhash.Window.Add_SourceInitialized({
 +        #Create Timer object
 +        Write-Verbose "Creating timer object"
 +        $Script:timer = new-object System.Windows.Threading.DispatcherTimer 
 +        #Fire off every 1 minutes
 +        Write-Verbose "Adding 1 minute interval to timer object"
 +        $timer.Interval = [TimeSpan]"0:0:1.00"
 +        #Add event per tick
 +        Write-Verbose "Adding Tick Event to timer object"
 +        $timer.Add_Tick({
 +        $Update.Invoke()
 +        [Windows.Input.InputEventHandler]{ $Clockhash.Window.UpdateLayout() }
 +    
 +    })
 +        #Start timer
 +        Write-Verbose "Starting Timer"
 +        $timer.Start()
 +        If (-NOT $timer.IsEnabled) {
 +            $Clockhash.Window.Close()
 +        }
 +    }) 
 +
 +    $Clockhash.Window.Add_Closed({
 +        $timer.Stop()
 +        $Runspacehash.PowerShell.Dispose()
 +    
 +        [gc]::Collect()
 +        [gc]::WaitForPendingFinalizers()    
 +    })
 +    $Clockhash.month_txtbx.Add_SizeChanged({
 +        [int]$clockhash.length = [math]::Round(($Clockhash.day_txtbx.ActualWidth,$Clockhash.month_txtbx.ActualWidth | 
 +            Sort -Descending)[0])
 +        [int]$Adjustment = $clockhash.length + 52 + 10 #Hard coded margin plus white space
 +    
 +        $YearMargin = $Clockhash.year_txtbx.Margin
 +        $Clockhash.year_txtbx.Margin = ("{0},{1},{2},{3}" -f ($Adjustment),
 +            $YearMargin.Top,$YearMargin.Right,$YearMargin.Bottom)
 +    })
 +    $Clockhash.time_txtbox.Add_SizeChanged({
 +        If ($Clockhash.time_txtbox.text.length -eq 4) {        
 +            $Clockhash.ampm_txtbx.Margin  = "133,0,86,0"
 +        } Else {
 +            $Clockhash.ampm_txtbx.Margin  = "172,0,48,0"
 +        }     
 +    })
 +    $Clockhash.Window.Add_MouseRightButtonUp({
 +        $This.close()
 +    })
 +    $Clockhash.Window.Add_MouseLeftButtonDown({
 +        $This.DragMove()
 +    })
 +    $Update.Invoke()
 +    $Clockhash.Window.ShowDialog() | Out-Null
 +    }).BeginInvoke() 
 +}
 +
 +Start-WpfUhr
 +
 +#endregion
 +#region .NET: Read-Window (WinForm, CmdLet)
 +
 +function Read-Window
 +{
 +    <#
 +        .EXAMPLE
 +            Read-Window
 + 
 +        .EXAMPLE
 +            Read-Window -Title "BANK-O-MAT" -Message "Wieviel wollen Sie abheben?" -DefaultText 100
 +    #>
 +    
 +    Param(
 +        [string]$Title = [string]::Empty,
 +        [string]$Message = [string]::Empty,
 +        [string]$DefaultText = [string]::Empty
 +    )
 +
 +    $a = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
 +    $b = [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
 +    
 +    $messageLabel = New-Object System.Windows.Forms.Label
 +    $messageLabel.Text   = $Message
 +    $messageLabel.Left   = 10
 +    $messageLabel.Top    = 10
 +    $messageLabel.Width  = 250
 +    $messageLabel.Height = 30
 +    $messageLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
 +    #$messageLabel.BackColor = [System.Drawing.Color]::Pink # DEBUG-Farbe
 +
 +    $inputTextBox = New-Object System.Windows.Forms.TextBox
 +    $inputTextBox.Left      = $messageLabel.Left
 +    $inputTextBox.Top       = $messageLabel.Top + $messageLabel.Height + 5
 +    $inputTextBox.Width     = $messageLabel.Width
 +    $inputTextBox.Multiline = $true
 +    $inputTextBox.Height    = 20
 +    $inputTextBox.Text      = $DefaultText
 +    $inputTextBox.Anchor    = [System.Windows.Forms.AnchorStyles]::Left -bor `
 +                              [System.Windows.Forms.AnchorStyles]::Top -bor `
 +                              [System.Windows.Forms.AnchorStyles]::Right -bor `
 +                              [System.Windows.Forms.AnchorStyles]::Bottom
 +
 +    $okButtom = New-Object System.Windows.Forms.Button
 +    $okButtom.Text         = "&OK"
 +    $okButtom.Left         = 105
 +    $okButtom.Top          = $inputTextBox.Top + $inputTextBox.Height + 10
 +    $okButtom.DialogResult = [System.Windows.Forms.DialogResult]::OK
 +    $okButtom.Anchor       = [System.Windows.Forms.AnchorStyles]::Right -bor `
 +                             [System.Windows.Forms.AnchorStyles]::Bottom
 +
 +    $cancelButton = New-Object System.Windows.Forms.Button
 +    $cancelButton.Text         = "&Abbruch"
 +    $cancelButton.Left         = $okButtom.Left + $okButtom.Width + 5
 +    $cancelButton.Top          = $okButtom.Top
 +    $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
 +    $cancelButton.Anchor       = [System.Windows.Forms.AnchorStyles]::Right -bor `
 +                                 [System.Windows.Forms.AnchorStyles]::Bottom
 +
 +    $inputboxForm = New-Object System.Windows.Forms.Form
 +    $inputboxForm.AcceptButton = $okButtom
 +    $inputboxForm.CancelButton = $cancelButton
 +    $inputboxForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
 +    $inputboxForm.MinimizeBox = $false
 +    $inputboxForm.MaximizeBox = $false
 +    $inputboxForm.Text        = $Title
 +    $inputboxForm.Height      = 180
 +    $inputboxForm.Controls.AddRange(($messageLabel, $inputTextBox, $okButtom, $cancelButton))
 +    $inputboxForm.Scale(3)
 +
 +    $formClosingAction = {
 +        if($inputboxForm.DialogResult -eq [System.Windows.Forms.DialogResult]::OK)
 +        {
 +            Out-Host -InputObject $inputTextBox.Text
 +        }
 +        else
 +        {
 +            Out-Host -InputObject ([string]::Empty)
 +        }
 +    }
 +    $inputboxForm.Add_FormClosing($formClosingAction)
 +    
 +    $inputboxForm.ShowDialog() | Out-Null
 +}
 +
 +#endregion
 +#region UI-Abfrage per PromptForChoice
 +
 +$title = "Delete Files"
 +$message = "Do you want to delete the remaining files in the folder?"
 +
 +$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
 +    "Deletes all the files in the folder."
 +
 +$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
 +    "Retains all the files in the folder."
 +
 +$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
 +
 +$result = $host.ui.PromptForChoice($title, $message, $options, 0) 
 +
 +switch ($result)
 +{
 +    0 {"You selected Yes."}
 +    1 {"You selected No."}
 +}
 +
 +#endregion
 +#region Diagramm erstellen per DataVisualization
 +
 +using namespace System.Windows.Forms.DataVisualization.Charting
 +using namespace System.Windows.Forms
 +
 +[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")
 +
 +$datasource = Get-Process | sort PrivateMemorySize -Descending  | Select-Object -First 5
 +
 +$chart1 = New-object Chart
 +$chart1.Width = 1000
 +$chart1.Height = 1000
 +$chart1.BackColor = [System.Drawing.Color]::White
 +$chart1.Dock = [DockStyle]::Fill
 +
 +$chart1.Titles.Add("Top 5 - Memory Usage (as: Column)") | Out-Null
 +$chart1.Titles[0].Font = "Arial,13pt"
 +$chart1.Titles[0].Alignment = "topLeft"
 +
 +$chartarea = New-Object ChartArea
 +$chartarea.Name = "ChartArea1"
 +$chartarea.AxisY.Title = "Memory (MB)"
 +$chartarea.AxisX.Title = "Process Name"
 +$chartarea.AxisY.Interval = 100
 +$chartarea.AxisX.Interval = 1
 +$chart1.ChartAreas.Add($chartarea)
 +
 +$legend = New-Object system.Windows.Forms.DataVisualization.Charting.Legend
 +$legend.name = "Legend1"
 +$chart1.Legends.Add($legend)
 +
 +$chart1.Series.Add("VirtualMem") | Out-Null
 +$chart1.Series["VirtualMem"].ChartType = "Column"
 +$chart1.Series["VirtualMem"].BorderWidth  = 3
 +$chart1.Series["VirtualMem"].IsVisibleInLegend = $true
 +$chart1.Series["VirtualMem"].chartarea = "ChartArea1"
 +$chart1.Series["VirtualMem"].Legend = "Legend1"
 +$chart1.Series["VirtualMem"].color = "#62B5CC"
 +$datasource | ForEach-Object {
 +    $chart1.Series["VirtualMem"].Points.addxy( $_.Name , ($_.VirtualMemorySize / 1MB))  | Out-Null}
 +
 +$chart1.Series.Add("PrivateMem") | Out-Null
 +$chart1.Series["PrivateMem"].ChartType = "Column"
 +$chart1.Series["PrivateMem"].IsVisibleInLegend = $true
 +$chart1.Series["PrivateMem"].BorderWidth  = 3
 +$chart1.Series["PrivateMem"].chartarea = "ChartArea1"
 +$chart1.Series["PrivateMem"].Legend = "Legend1"
 +$chart1.Series["PrivateMem"].color = "#E3B64C"
 +$datasource | ForEach-Object {
 +    $chart1.Series["PrivateMem"].Points.addxy( $_.Name , ($_.PrivateMemorySize / 1MB)) | Out-Null }
 +
 +# z.B. als PNG speichern, oder
 +$chart1.SaveImage("c:\temp\SplineArea.png", "png")
 +
 +# z.B. im Fenster anzeigen
 +$Form = New-Object Form
 +$Form.Width = 1024
 +$Form.Height = 820
 +$Form.controls.add($chart1)
 +$Form.Add_Shown({$Form.Activate()})
 +$Form.ShowDialog()
 +
 +#endregion
 +
 +#endregion
 +
 +#endregion
 +#region EINARBEITEN
 +
 +function Join-Object
 +{
 +    <#
 +    .SYNOPSIS
 +        Join data from two sets of objects based on a common value
 + 
 +    .DESCRIPTION
 +        Join data from two sets of objects based on a common value
 + 
 +        For more details, see the accompanying blog post:
 +            http://ramblingcookiemonster.github.io/Join-Object/
 + 
 +        For even more details, see the original code and discussions that this borrows from:
 +            Dave Wyatt's Join-Object - http://powershell.org/wp/forums/topic/merging-very-large-collections
 +            Lucio Silveira's Join-Object - http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx
 + 
 +    .PARAMETER Left
 +        'Left' collection of objects to join. You can use the pipeline for Left.
 + 
 +        The objects in this collection should be consistent.
 +        We look at the properties on the first object for a baseline.
 +     
 +    .PARAMETER Right
 +        'Right' collection of objects to join.
 + 
 +        The objects in this collection should be consistent.
 +        We look at the properties on the first object for a baseline.
 + 
 +    .PARAMETER LeftJoinProperty
 +        Property on Left collection objects that we match up with RightJoinProperty on the Right collection
 + 
 +    .PARAMETER RightJoinProperty
 +        Property on Right collection objects that we match up with LeftJoinProperty on the Left collection
 + 
 +    .PARAMETER LeftProperties
 +        One or more properties to keep from Left. Default is to keep all Left properties (*).
 + 
 +        Each property can:
 +            - Be a plain property name like "Name"
 +            - Contain wildcards like "*"
 +            - Be a hashtable like @{Name="Product Name";Expression={$_.Name}}.
 +                 Name is the output property name
 +                 Expression is the property value ($_ as the current object)
 +                 
 +                 Alternatively, use the Suffix or Prefix parameter to avoid collisions
 +                 Each property using this hashtable syntax will be excluded from suffixes and prefixes
 + 
 +    .PARAMETER RightProperties
 +        One or more properties to keep from Right. Default is to keep all Right properties (*).
 + 
 +        Each property can:
 +            - Be a plain property name like "Name"
 +            - Contain wildcards like "*"
 +            - Be a hashtable like @{Name="Product Name";Expression={$_.Name}}.
 +                 Name is the output property name
 +                 Expression is the property value ($_ as the current object)
 +                 
 +                 Alternatively, use the Suffix or Prefix parameter to avoid collisions
 +                 Each property using this hashtable syntax will be excluded from suffixes and prefixes
 + 
 +    .PARAMETER Prefix
 +        If specified, prepend Right object property names with this prefix to avoid collisions
 + 
 +        Example:
 +            Property Name = 'Name'
 +            Suffix = 'j_'
 +            Resulting Joined Property Name = 'j_Name'
 + 
 +    .PARAMETER Suffix
 +        If specified, append Right object property names with this suffix to avoid collisions
 + 
 +        Example:
 +            Property Name = 'Name'
 +            Suffix = '_j'
 +            Resulting Joined Property Name = 'Name_j'
 + 
 +    .PARAMETER Type
 +        Type of join. Default is AllInLeft.
 + 
 +        AllInLeft will have all elements from Left at least once in the output, and might appear more than once
 +          if the where clause is true for more than one element in right, Left elements with matches in Right are
 +          preceded by elements with no matches.
 +          SQL equivalent: outer left join (or simply left join)
 + 
 +        AllInRight is similar to AllInLeft.
 +         
 +        OnlyIfInBoth will cause all elements from Left to be placed in the output, only if there is at least one
 +          match in Right.
 +          SQL equivalent: inner join (or simply join)
 +          
 +        AllInBoth will have all entries in right and left in the output. Specifically, it will have all entries
 +          in right with at least one match in left, followed by all entries in Right with no matches in left,
 +          followed by all entries in Left with no matches in Right.
 +          SQL equivalent: full join
 + 
 +    .EXAMPLE
 +        #
 +        #Define some input data.
 + 
 +        $l = 1..5 | Foreach-Object {
 +            [pscustomobject]@{
 +                Name = "jsmith$_"
 +                Birthday = (Get-Date).adddays(-1)
 +            }
 +        }
 + 
 +        $r = 4..7 | Foreach-Object{
 +            [pscustomobject]@{
 +                Department = "Department $_"
 +                Name = "Department $_"
 +                Manager = "jsmith$_"
 +            }
 +        }
 + 
 +        #We have a name and Birthday for each manager, how do we find their department, using an inner join?
 +        Join-Object -Left $l -Right $r -LeftJoinProperty Name -RightJoinProperty Manager -Type OnlyIfInBoth -RightProperties Department
 + 
 + 
 +            # Name Birthday Department
 +            # ---- -------- ----------
 +            # jsmith4 4/14/2015 3:27:22 PM Department 4
 +            # jsmith5 4/14/2015 3:27:22 PM Department 5
 + 
 +    .EXAMPLE
 +        #
 +        #Define some input data.
 + 
 +        $l = 1..5 | Foreach-Object {
 +            [pscustomobject]@{
 +                Name = "jsmith$_"
 +                Birthday = (Get-Date).adddays(-1)
 +            }
 +        }
 + 
 +        $r = 4..7 | Foreach-Object{
 +            [pscustomobject]@{
 +                Department = "Department $_"
 +                Name = "Department $_"
 +                Manager = "jsmith$_"
 +            }
 +        }
 + 
 +        #We have a name and Birthday for each manager, how do we find all related department data, even if there are conflicting properties?
 +        $l | Join-Object -Right $r -LeftJoinProperty Name -RightJoinProperty Manager -Type AllInLeft -Prefix j_
 + 
 +            # Name Birthday j_Department j_Name j_Manager
 +            # ---- -------- ------------ ------ ---------
 +            # jsmith1 4/14/2015 3:27:22 PM
 +            # jsmith2 4/14/2015 3:27:22 PM
 +            # jsmith3 4/14/2015 3:27:22 PM
 +            # jsmith4 4/14/2015 3:27:22 PM Department 4 Department 4 jsmith4
 +            # jsmith5 4/14/2015 3:27:22 PM Department 5 Department 5 jsmith5
 + 
 +    .EXAMPLE
 +        #
 +        #Hey! You know how to script right? Can you merge these two CSVs, where Path1's IP is equal to Path2's IP_ADDRESS?
 +         
 +        #Get CSV data
 +        $s1 = Import-CSV $Path1
 +        $s2 = Import-CSV $Path2
 + 
 +        #Merge the data, using a full outer join to avoid omitting anything, and export it
 +        Join-Object -Left $s1 -Right $s2 -LeftJoinProperty IP_ADDRESS -RightJoinProperty IP -Prefix 'j_' -Type AllInBoth |
 +            Export-CSV $MergePath -NoTypeInformation
 + 
 +    .EXAMPLE
 +        #
 +        # "Hey Warren, we need to match up SSNs to Active Directory users, and check if they are enabled or not.
 +        # I'll e-mail you an unencrypted CSV with all the SSNs from gmail, what could go wrong?"
 +         
 +        # Import some SSNs.
 +        $SSNs = Import-CSV -Path D:\SSNs.csv
 + 
 +        #Get AD users, and match up by a common value, samaccountname in this case:
 +        Get-ADUser -Filter "samaccountname -like 'wframe*'" |
 +            Join-Object -LeftJoinProperty samaccountname -Right $SSNs `
 +                        -RightJoinProperty samaccountname -RightProperties ssn `
 +                        -LeftProperties samaccountname, enabled, objectclass
 + 
 +    .NOTES
 +        This borrows from:
 +            Dave Wyatt's Join-Object - http://powershell.org/wp/forums/topic/merging-very-large-collections/
 +            Lucio Silveira's Join-Object - http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx
 + 
 +        Changes:
 +            Always display full set of properties
 +            Display properties in order (left first, right second)
 +            If specified, add suffix or prefix to right object property names to avoid collisions
 +            Use a hashtable rather than ordereddictionary (avoid case sensitivity)
 + 
 +    .LINK
 +        http://ramblingcookiemonster.github.io/Join-Object/
 + 
 +    .FUNCTIONALITY
 +        PowerShell Language
 + 
 +    #>
 +    [CmdletBinding()]
 +    Param
 +    (
 +        [Parameter(Mandatory=$true,
 +                   ValueFromPipeLine = $true)]
 +        [object[]] $Left,
 +
 +        # List to join with $Left
 +        [Parameter(Mandatory=$true)]
 +        [object[]] $Right,
 +
 +        [Parameter(Mandatory = $true)]
 +        [string] $LeftJoinProperty,
 +
 +        [Parameter(Mandatory = $true)]
 +        [string] $RightJoinProperty,
 +
 +        [object[]]$LeftProperties = '*',
 +
 +        # Properties from $Right we want in the output.
 +        # Like LeftProperties, each can be a plain name, wildcard or hashtable. See the LeftProperties comments.
 +        [object[]]$RightProperties = '*',
 +
 +        [validateset( 'AllInLeft', 'OnlyIfInBoth', 'AllInBoth', 'AllInRight')]
 +        [Parameter(Mandatory=$false)]
 +        [string]$Type = 'AllInLeft',
 +
 +        [string]$Prefix,
 +        [string]$Suffix
 +    )
 +    Begin
 +    {
 +        function AddItemProperties($item, $properties, $hash)
 +        {
 +            if ($null -eq $item)
 +            {
 +                return
 +            }
 +
 +            foreach($property in $properties)
 +            {
 +                $propertyHash = $property -as [hashtable]
 +                if($null -ne $propertyHash)
 +                {
 +                    $hashName = $propertyHash["name"] -as [string]         
 +                    $expression = $propertyHash["expression"] -as [scriptblock]
 +
 +                    $expressionValue = $expression.Invoke($item)[0]
 +            
 +                    $hash[$hashName] = $expressionValue
 +                }
 +                else
 +                {
 +                    foreach($itemProperty in $item.psobject.Properties)
 +                    {
 +                        if ($itemProperty.Name -like $property)
 +                        {
 +                            $hash[$itemProperty.Name] = $itemProperty.Value
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        function TranslateProperties
 +        {
 +            [cmdletbinding()]
 +            param(
 +                [object[]]$Properties,
 +                [psobject]$RealObject,
 +                [string]$Side)
 +
 +            foreach($Prop in $Properties)
 +            {
 +                $propertyHash = $Prop -as [hashtable]
 +                if($null -ne $propertyHash)
 +                {
 +                    $hashName = $propertyHash["name"] -as [string]         
 +                    $expression = $propertyHash["expression"] -as [scriptblock]
 +
 +                    $ScriptString = $expression.tostring()
 +                    if($ScriptString -notmatch 'param\(')
 +                    {
 +                        Write-Verbose "Property '$HashName'`: Adding param(`$_) to scriptblock '$ScriptString'"
 +                        $Expression = [ScriptBlock]::Create("param(`$_)`n $ScriptString")
 +                    }
 +                
 +                    $Output = @{Name =$HashName; Expression = $Expression }
 +                    Write-Verbose "Found $Side property hash with name $($Output.Name), expression:`n$($Output.Expression | out-string)"
 +                    $Output
 +                }
 +                else
 +                {
 +                    foreach($ThisProp in $RealObject.psobject.Properties)
 +                    {
 +                        if ($ThisProp.Name -like $Prop)
 +                        {
 +                            Write-Verbose "Found $Side property '$($ThisProp.Name)'"
 +                            $ThisProp.Name
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        function WriteJoinObjectOutput($leftItem, $rightItem, $leftProperties, $rightProperties)
 +        {
 +            $properties = @{}
 +
 +            AddItemProperties $leftItem $leftProperties $properties
 +            AddItemProperties $rightItem $rightProperties $properties
 +
 +            New-Object psobject -Property $properties
 +        }
 +
 +        #Translate variations on calculated properties. Doing this once shouldn't affect perf too much.
 +        foreach($Prop in @($LeftProperties + $RightProperties))
 +        {
 +            if($Prop -as [hashtable])
 +            {
 +                foreach($variation in ('n','label','l'))
 +                {
 +                    if(-not $Prop.ContainsKey('Name') )
 +                    {
 +                        if($Prop.ContainsKey($variation) )
 +                        {
 +                            $Prop.Add('Name',$Prop[$Variation])
 +                        }
 +                    }
 +                }
 +                if(-not $Prop.ContainsKey('Name') -or $Prop['Name'] -like $null )
 +                {
 +                    Throw "Property is missing a name`n. This should be in calculated property format, with a Name and an Expression:`n@{Name='Something';Expression={`$_.Something}}`nAffected property:`n$($Prop | out-string)"
 +                }
 +
 +
 +                if(-not $Prop.ContainsKey('Expression') )
 +                {
 +                    if($Prop.ContainsKey('E') )
 +                    {
 +                        $Prop.Add('Expression',$Prop['E'])
 +                    }
 +                }
 +            
 +                if(-not $Prop.ContainsKey('Expression') -or $Prop['Expression'] -like $null )
 +                {
 +                    Throw "Property is missing an expression`n. This should be in calculated property format, with a Name and an Expression:`n@{Name='Something';Expression={`$_.Something}}`nAffected property:`n$($Prop | out-string)"
 +                }
 +            }        
 +        }
 +
 +        $leftHash = @{}
 +        $rightHash = @{}
 +
 +        # Hashtable keys can't be null; we'll use any old object reference as a placeholder if needed.
 +        $nullKey = New-Object psobject
 +        
 +        $bound = $PSBoundParameters.keys -contains "InputObject"
 +        if(-not $bound)
 +        {
 +            [System.Collections.ArrayList]$LeftData = @()
 +        }
 +    }
 +    Process
 +    {
 +        #We pull all the data for comparison later, no streaming
 +        if($bound)
 +        {
 +            $LeftData = $Left
 +        }
 +        Else
 +        {
 +            foreach($Object in $Left)
 +            {
 +                [void]$LeftData.add($Object)
 +            }
 +        }
 +    }
 +    End
 +    {
 +        foreach ($item in $Right)
 +        {
 +            $key = $item.$RightJoinProperty
 +
 +            if ($null -eq $key)
 +            {
 +                $key = $nullKey
 +            }
 +
 +            $bucket = $rightHash[$key]
 +
 +            if ($null -eq $bucket)
 +            {
 +                $bucket = New-Object System.Collections.ArrayList
 +                $rightHash.Add($key, $bucket)
 +            }
 +
 +            $null = $bucket.Add($item)
 +        }
 +
 +        foreach ($item in $LeftData)
 +        {
 +            $key = $item.$LeftJoinProperty
 +
 +            if ($null -eq $key)
 +            {
 +                $key = $nullKey
 +            }
 +
 +            $bucket = $leftHash[$key]
 +
 +            if ($null -eq $bucket)
 +            {
 +                $bucket = New-Object System.Collections.ArrayList
 +                $leftHash.Add($key, $bucket)
 +            }
 +
 +            $null = $bucket.Add($item)
 +        }
 +
 +        $LeftProperties = TranslateProperties -Properties $LeftProperties -Side 'Left' -RealObject $LeftData[0]
 +        $RightProperties = TranslateProperties -Properties $RightProperties -Side 'Right' -RealObject $Right[0]
 +
 +        #I prefer ordered output. Left properties first.
 +        [string[]]$AllProps = $LeftProperties
 +
 +        #Handle prefixes, suffixes, and building AllProps with Name only
 +        $RightProperties = foreach($RightProp in $RightProperties)
 +        {
 +            if(-not ($RightProp -as [Hashtable]))
 +            {
 +                Write-Verbose "Transforming property $RightProp to $Prefix$RightProp$Suffix"
 +                @{
 +                    Name="$Prefix$RightProp$Suffix"
 +                    Expression=[scriptblock]::create("param(`$_) `$_.'$RightProp'")
 +                }
 +                $AllProps += "$Prefix$RightProp$Suffix"
 +            }
 +            else
 +            {
 +                Write-Verbose "Skipping transformation of calculated property with name $($RightProp.Name), expression:`n$($RightProp.Expression | out-string)"
 +                $AllProps += [string]$RightProp["Name"]
 +                $RightProp
 +            }
 +        }
 +
 +        $AllProps = $AllProps | Select -Unique
 +
 +        Write-Verbose "Combined set of properties: $($AllProps -join ', ')"
 +
 +        foreach ( $entry in $leftHash.GetEnumerator() )
 +        {
 +            $key = $entry.Key
 +            $leftBucket = $entry.Value
 +
 +            $rightBucket = $rightHash[$key]
 +
 +            if ($null -eq $rightBucket)
 +            {
 +                if ($Type -eq 'AllInLeft' -or $Type -eq 'AllInBoth')
 +                {
 +                    foreach ($leftItem in $leftBucket)
 +                    {
 +                        WriteJoinObjectOutput $leftItem $null $LeftProperties $RightProperties | Select $AllProps
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                foreach ($leftItem in $leftBucket)
 +                {
 +                    foreach ($rightItem in $rightBucket)
 +                    {
 +                        WriteJoinObjectOutput $leftItem $rightItem $LeftProperties $RightProperties | Select $AllProps
 +                    }
 +                }
 +            }
 +        }
 +
 +        if ($Type -eq 'AllInRight' -or $Type -eq 'AllInBoth')
 +        {
 +            foreach ($entry in $rightHash.GetEnumerator())
 +            {
 +                $key = $entry.Key
 +                $rightBucket = $entry.Value
 +
 +                $leftBucket = $leftHash[$key]
 +
 +                if ($null -eq $leftBucket)
 +                {
 +                    foreach ($rightItem in $rightBucket)
 +                    {
 +                        WriteJoinObjectOutput $null $rightItem $LeftProperties $RightProperties | Select $AllProps
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#region Übungen
 +
 +$request = Invoke-WebRequest -Uri "http://www.bing.com/HPImageArchive.aspx?mbl=1&mkt=de-DE&idx=0&n=1"
 +$xImages = [xml]$request.Content
 +$xImages.images.image.url
 +$xImages.images.image.copyright
 +$url2 = "http://www.bing.com//az/hprichbg/rb/BerchtesgadenAlps_DE-DE12607487459_1366x768.jpg"
 +Invoke-WebRequest -Uri $url2 -OutFile C:\PowerShell\test.jpg
 +
 +
 +
 +# Alle *.temp löschen die älter sind als 1 Minuten
 +
 +
 +
 +<#
 +    ÜBUNG
 +        1.) Für den aktuellen Benutzer einen Schlüssel erstellen "gfu" in Software
 +        2.) Eigenschaft Erstellungsdatum mit Datum von heute
 +        2.) NUR das Datum anzeigen lassen und darauf 5 Monate addieren
 +#>
 +
 +
 +<#
 +    ÜBUNG "Was kann ich mit Datei-Objekten anfangen"
 + 
 +    a) Finden Sie alle (auch Versteckte) große (> 5MB) Dateien
 +       in c:\temp und Unterordner um diese zu löschen.
 +    b) Kürzen Sie den Aufruf auf ein maximum
 +    C) Finden Sie zum löschen eine Variante als CmdLet
 +         
 +    Hinweise:
 +        a) Get-ChildItem findet NUR ALLE Dateien auch in Unterordner (siehe Hilfe)
 +        b) Erstellen Sie für Test-Zwecke z.B. große TXT-Dateien in den o.a. Ordnern
 +        c) Das Where-Object filtert z.B. nach Datei-Größe
 +        d) Get-Member zeigt Ihnen welche Eigenschaft die Größe (in Byte) enthält
 +        e) Get-Member zeigt Ihnen die Methode zum löschen
 +        f) ()-Paar spielt eine entscheidente Rolle
 +#>
 +
 +<#
 +    ÜBUNG "Was kann ich mit Datei-Objekten anfangen"
 + 
 +    a) Finden Sie alle (auch Versteckte) große (> 5MB) Dateien
 +       in c:\temp und Unterordner um diese zu löschen.
 +    b) Kürzen Sie den Aufruf auf ein maximum
 +    C) Finden Sie zum löschen eine Variante als CmdLet
 +         
 +    Hinweise:
 +        a) Get-ChildItem findet NUR ALLE Dateien auch in Unterordner (siehe Hilfe)
 +        b) Erstellen Sie für Test-Zwecke z.B. große TXT-Dateien in den o.a. Ordnern
 +        c) Das Where-Object filtert z.B. nach Datei-Größe
 +        d) Get-Member zeigt Ihnen welche Eigenschaft die Größe (in Byte) enthält
 +        e) Get-Member zeigt Ihnen die Methode zum löschen
 +        f) ()-Paar spielt eine entscheidente Rolle
 +#>
 +
 +<#
 +    ÜBUNG: "Umgang mit PS-Provider'n und Filterung"
 + 
 +    a) Finden Sie alle Schlüssel die mit Run beginnen in HKey Current User
 +    b) Finden Sie alle Schlüssel die mit Run beginnen in HKey Current User
 +       und HKey Local Machine AUF EINMAL !!!!
 + 
 +    HINWEISE
 +        Get-PSDrive, Get-ChildItem, Where-Object, Get-Member, Format-List
 +        führen zum Erfolg !!!
 +                   ******
 +#>
 +
 +<#
 +    ÜBUNG: "Module"
 + 
 +    a) Ist das ActiveDirectory Modul installiert?
 +    b) Wie lautet das CmdLet um einen Benutzer im AD zu ändern?
 +    n) Wieviele CmdLets aus dem ActiveDirectory-Modul gibt es?
 +#>
 +
 +<#
 +    ÜBUNG: "Module"
 + 
 +    (PowerShell Community Extensions) http://pscx.codeplex.com
 + 
 +    a) Installieren Sie das PSCX-Modul
 +    b) Welche sinnvollen CmdLets enthält diese Modul für SIE?
 +#>
 +
 +<#
 +    ÜBUNG "Remoting"
 + 
 +    a) Sammeln Sie von min. 2 Remotecomputern die MAC-Adresse über das
 +       CmdLet Invoke-Command
 +    b) und geben eine Tabelle zurück mit den Spalten für
 +        PowerShell-RemoteComputername,
 +        ComputerNamen und
 +        MAC-Adresse
 +    c) Die MAC-Adresse soll ohne "-" angezeigt werden
 +#>
 +#endregion
 +
 +
 +#endregion
 </code> </code>
 =====Links===== =====Links=====
coding/powershell.1740657802.txt.gz · Zuletzt geändert: 2025/02/27 13:03 von jango