Wissen/A01_Spickzettel.ps1
# ? TITEL Spickzettel # ? DESCRIPTION PowerShell Spickzettel # ? TAGS Spickzettel Kochbuch Tips # ? VERSION 2010.11.11 [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '')] param() #region Analyse & Hilfe # ? 1. PowerShell-Hilfe installieren / aktualisieren Update-Help -Module * -UICulture en-US, de-DE -Force # ? 2. Hilfe zur bzw. um die PowerShell (about-Seiten) Get-Help -Name about_* | Out-GridView -OutputMode Multiple # ! Inhaltsverzeichnis Get-Help -Name about_CommonParameters -ShowWindow # ! about_CommonParameters öffnen Get-Help -Name "Call operator" -Category HelpFile # ! In allen about-Seiten suchen # ? 3. Ein benötigtes Cmdlet finden Get-Command -Name * -CommandType Cmdlet, Function # ! Ein Cmdlet über dessen Namen finden Get-Command -Verb Stop # ! Ein Cmdlet über dessen Verb finden Get-Verb | Sort-Object -Property Verb | Format-Wide -AutoSize # ! Welche Verben gibt es Get-Command -Noun Object # ! Ein Cmdlet über dessen Substantiv finden Get-Command -Name G*-*connection* # ! ? Oder eine Suchkombination aus Verb und Noun Get-Command -Name * -Module RemoteDesktop # ! Ein Cmdlet finden über den Modulnamen Get-Module -ListAvailable | Out-GridView # ! Welche Module gibt es Start-Process -FilePath https://www.google.de/search?q=powershell+ping # ! Ein Cmdlet über Google finden # ? 4. Hilfe zu einem Cmdlet aufrufen, lesen und anwenden Get-Help -Name Get-Process -ShowWindow # ! Hilfe für Get-Process LOKAL öffnen Get-Help -Name Get-Process -Online # ! Hilfe für Get-Process ONLINE öffnen (VSCode: CTRL + F1) Get-Help -Name about_CommonParameters -ShowWindow # ! Hilfe zu den Standard-Parametern # ? 5. Objekt-Analyse: # ? AUFGABENSTELLUNG: Welche Prozesse sind von der Firma Microsoft? # ! Es ist immer wichtig zu analysieren (u.a. Get-Member, Select-Object) # ! A) was ist das für ein Rückgabeobjekt, Get-Process | Get-Member Get-Process | Get-Member -View All Get-Process | Get-Member -View Extended # ! B) was kann ich mit diesem Objekte machen (Eigenschaften, Methoden, Ereignisse) und Get-Process | Get-Member # ! => ProcessName, Company, Description, Product, Site # ! C) welche Werte enthalten diese Eigenschaften? Get-Process | Select-Object -Property ProcessName, Company, Description, Product, Site # ? LÖSUNG: Get-Process | Where-Object -Property Company -EQ -Value "Microsoft Corporation" # ? 6. Vererbung # ! 1. Eine Klasse kann von EINER anderen Basisklasse ABGELEITET sein und ERBT so dessen Member/Funktionen. # ! 2. Jede Klasse ist früher oder später von der Klasse 'Object' (alias PSObject) abgeleitet worden. # ! 3. Eine abgeleitete Klasse A ist daher mit der Basisklasse B kompatibel, # ! daher kann auch ein Objekt von Typ A an einen Parameter vom Typ B übergeben werden. # ? Welche Typ A von welchem Typ B abgeleitet wurde erfahren Sie so: "Köln!".PSTypeNames (Get-Process)[0].PSTypeNames (Get-Service)[0].PSTypeNames (Get-Process).PSTypeNames # ? 7. Enumerationen # ! Enumerationen (Enum) bilden eine häufig benutzt Untermenge von Typen da. # ! Ob ein Parameter eben nur diese Enumeration akzeptiert ist auf den ersten Blick ... # ! ... nicht ersichtlich da die Hilfe nur den Typennamen ausgibt. # ? Handelt es sich bei einem Type X um eine Enumeration? Get-Service | Get-Member -Name StartType [System.ServiceProcess.ServiceStartMode] | Get-Member [System.ServiceProcess.ServiceStartMode].IsEnum [System.ServiceProcess.ServiceStartMode].GetEnumNames() # ? Vorhandene Enumerations-Typen finden function Get-Enum { param ( [string]$Value, [String]$Name, [switch]$All ) $defaultManifestModules = 'CommonLanguageRuntimeLibrary', 'Microsoft.CSharp.dll', 'Microsoft.Management.Infrastructure.dll', 'Microsoft.PowerShell.Commands.Management.dll', 'Microsoft.PowerShell.commands.Utility.dll', 'System.dll', 'System.Configuration.dll', 'System.Configuration.Install.dll', 'System.Core.dll', 'System.Data.dll', 'System.DirectoryServices.dll', 'System.Management.Automation.dll', 'System.Management.dll', 'System.ServiceProcess.dll', 'System.Transactions.dll', 'System.Xml.dll' [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object -FilterScript {$All -or ($defaultManifestModules -contains $_.ManifestModule)} | ForEach-Object -Process { try { $_.GetExportedTypes() } catch { "Keine ExportedTypes vorhanden" | Write-Verbose } } | Where-Object -FilterScript { $_.IsEnum -and $_.Name -imatch $Name } | ForEach-Object -Process { return [PSCustomObject]@{ Name = $_.FullName Source = $_.Module.ScopeName Values = [System.Enum]::GetNames($_) } } | Where-Object -Property Values -imatch -Value $Value } Get-Enum -Value SilentlyContinue Get-Enum -Name ServiceStartMode | Get-Member Get-Enum -All | Measure-Object # ? 8. Weitere hilfreiche Informationen über Objekte herausfinden # ! Beide Methoden werden automatisch bei laden des AKPT-Moduls implementiert $code = { Add-Type -AssemblyName System.Windows.Forms $propertyGrid = New-Object System.Windows.Forms.PropertyGrid $propertyGrid.Dock = [System.Windows.Forms.DockStyle]::Fill $propertyGrid.PropertySort = [System.Windows.Forms.PropertySort]::Alphabetical $propertyGrid.SelectedObject = $this $window = New-Object System.Windows.Forms.Form $window.Text = $this.ToString() $window.Size = New-Object System.Drawing.Size -ArgumentList @(600, 800) $window.TopMost = $true $window.Controls.Add($propertyGrid) $window.ShowDialog() | Out-Null } Update-TypeData -MemberType ScriptMethod -MemberName ShowObject -Value $code -TypeName System.Object -Force $code = { $url = 'https://docs.microsoft.com/de-de/dotnet/api/{0}' -f $this.GetType().FullName Start-Process $url } Update-TypeData -MemberType ScriptMethod -MemberName GetHelp -Value $code -TypeName System.Object $p1 = Get-Process | Select-Object -First 1 $p1.ShowObject() $p1.GetHelp() #endregion #region Aliase # ! Nachteil ohne Aliase Get-Process | Where-Object -Property Company -Like -Value "Microsoft*" | Sort-Object -Property Name | Format-Table -Property Name, Company # ! Vorteil mit Aliase gp | ? Company -like "Microsoft*" | sort Name | ft Name, Company Get-Alias ; Get-Command -CommandType Alias # ! Übersicht aller Aliase Get-Alias -Name ls # ! Wie lautet das Cmdlet für den Alias 'ls' Get-Alias -Definition Get-ChildItem # ! Welche Aliase gibt es für das Cmdlet 'Get-ChildItem' New-Alias -Name ping -Value Test-NetConnection # ! Eigene Aliase erstellen # ! Für Parameter können auch Aliase definiert sein, welche es sein können erfahren Sie so: Get-Command -Name Get-ChildItem | Select-Object -ExpandProperty Parameters | Select-Object -ExpandProperty Values | Select-Object -Property Name, Aliases #endregion #region Objekte in der Pipeline managen Get-Command -Noun Object -Module Microsoft.PowerShell.* # ! Eine Übersicht von Cmdlets die Rückgabe-Objekte managen können: Get-Process | Sort-Object -Property WorkingSet64 -Descending # ! Sortieren Get-Process | Select-Object -Property Name, WorkingSet64 # ! Filtern Get-Process | Select-Object -Property Name -Unique # ! Filtern Get-Process | Select-Object -ExpandProperty Name # ! Filtern Get-Process | Select-Object -Skip 3 -First 2 # ! Filtern Get-Process | Where-Object -Property Company -Like -Value "Microsoft*" # ! Filtern Get-Process | Where-Object -FilterScript { $_.Company -like "Microsoft*" -or $_.Company -like "Oracle*"} # ! Filtern Get-ChildItem -Path C:\Windows -File -Force | Group-Object -Property Extension | # ! Gruppieren Sort-Object -Property Count -Descending | # ! Sortieren Select-Object -First 3 # ! Filtern Compare-Object -ReferenceObject 10,11,12,14 -DifferenceObject 10,12,13,14 # ! Vergleichen 31, 32, 33| ForEach-Object -Process {"192.168.178.$_"} # ! Objekt-Schleife # ? Objekte konvertieren Get-Command -Verb ConvertTo, ConvertFrom -Module Microsoft.PowerShell.* # ! Übersicht der Cmdlets Get-Process | ConvertTo-Csv | out-File C:\temp\procs.csv # ! CSV Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Csv -Delimiter ";" -NoTypeInformation | # ! CSV Set-Content C:\temp\procs.csv Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Html | out-File C:\temp\procs.html # ! HTml ConvertTo-SecureString -String 'P@ssw0rd' -AsPlainText -Force # ! SecureString #endregion #region Vorhandene Objekte erweitern # ? Vorhandene Objekt-Member abfragen Get-Process | Get-Member Get-Process | Get-Member -Static Get-Process | Get-Member -View All Get-Process | Get-Member -View Extended # ? Einem Process-Objekt temporär Member hinzufügen $p1 = Get-Process | Select-Object -First 1 | Add-Member -Name Jetzt_A -Value ( Get-Date ) -MemberType NoteProperty -PassThru | Add-Member -Name Jetzt_B -Value ({ Get-Date }) -MemberType ScriptMethod -PassThru | Add-Member -Name Jetzt_C -Value ({ Get-Date }) -MemberType ScriptProperty -PassThru $p1 | Get-Member -Name Jetzt* # ? # ? Für die aktuelle Session dauerhaft Erweiterungen vornehmen # ? # ! Beispiel 1: Update-TypeData -TypeName System.Diagnostics.Process -MemberName WorkingSet64MB -Value { [int]($this.WorkingSet64 / 1MB) } -MemberType ScriptProperty -Force Get-Process | Sort-Object -Property WorkingSet64MB -Descending | Select-Object -Property Name, WorkingSet64MB -First 10 # ! Beispiel 2: $code = { Add-Type -AssemblyName System.Windows.Forms $propertyGrid = New-Object System.Windows.Forms.PropertyGrid $propertyGrid.Dock = [System.Windows.Forms.DockStyle]::Fill $propertyGrid.PropertySort = [System.Windows.Forms.PropertySort]::Alphabetical $propertyGrid.SelectedObject = $this $window = New-Object System.Windows.Forms.Form $window.Text = $this.ToString() $window.Size = New-Object System.Drawing.Size -ArgumentList @(600, 800) $window.TopMost = $true $window.Controls.Add($propertyGrid) $window.ShowDialog() | Out-Null } Update-TypeData -MemberType ScriptMethod -MemberName ShowObject -Value $code -TypeName System.Object -Force $p1 = Get-Process | Select-Object -First 1 $p1.ShowObject() # ! Beispiel 3: $code = { $url = 'https://docs.microsoft.com/de-de/dotnet/api/{0}' -f $this.GetType().FullName Start-Process $url } Update-TypeData -MemberType ScriptMethod -MemberName GetHelp -Value $code -TypeName System.Object $p1 = Get-Process | Select-Object -First 1 $p1.GetHelp() #endregion #region Auf Ereignisse reagieren # ? Asynchrones Ereignis-Handling $np = Start-Process -FilePath notepad -WindowStyle Minimized -PassThru $regEvent = Register-ObjectEvent -InputObject $np -EventName Exited -Action { "Der Notepad-Prozess wurde beendet." | Write-Warning } $np | Stop-Process -Force $regEvent | Get-EventSubscriber | Unregister-Event # ? Synchrones Ereignis-Handling $np = Start-Process -FilePath notepad -WindowStyle Minimized -PassThru Register-ObjectEvent -InputObject $np -EventName Exited -SourceIdentifier np.Exited Wait-Event -SourceIdentifier np.Exited "... Die Verarbeitung nach dem Beenden von Notepad wurde aufgenommen." | Write-Warning Unregister-Event -SourceIdentifier np.Exited # ? Hintegrundjobs überwachen $job = Start-Job -Name gci -ScriptBlock { Get-ChildItem -Path c:\*.log -Recurse -Force -ErrorAction Ignore | Select-Object -Property Name, Length, DirectoryName } $job | Get-Member -MemberType Event Register-ObjectEvent -SourceIdentifier job.StateChanged -InputObject $job -EventName StateChanged -Action { "GCI-Job ist fertig, STATUS = $($job.State)" | Write-Warning Unregister-Event -SourceIdentifier job.StateChanged Remove-Job -Name job.StateChanged } $job | Receive-Job -Keep # ? Dateien- / Ordner auf Veränderung überwachen $fsw = New-Object -TypeName System.IO.FileSystemWatcher $fsw.Path = "c:\temp" $fsw.Filter = "*.txt" $fsw | Get-Member -MemberType Event $action = { "Dateiänderung: {0} {1}" -f $eventArgs.FullPath, $eventArgs.ChangeType | Write-Warning } Register-ObjectEvent -InputObject $fsw -EventName Changed -Action $action -SourceIdentifier fsw_Changed Register-ObjectEvent -InputObject $fsw -EventName Created -Action $action -SourceIdentifier fsw_Created Register-ObjectEvent -InputObject $fsw -EventName Deleted -Action $action -SourceIdentifier fsw_Deleted Register-ObjectEvent -InputObject $fsw -EventName Error -Action $action -SourceIdentifier fsw_Error Register-ObjectEvent -InputObject $fsw -EventName Renamed -Action $action -SourceIdentifier fsw_Renamed Get-EventSubscriber Unregister-Event -SourceIdentifier fsw_* -Force $fsw.Dispose() $fsw = $null Remove-Variable -Name fsw -Force # ? WMI Events $action = { $e = $Event.SourceEventArgs.NewEvent "Neuer Process gestartet, ID: {0} | NAME: {1}" -f $e.ProcessId, $e.ProcessName | Write-Warning } Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace -SourceIdentifier ProcessStarted -Action $action # ! ADMIN-Rechte Get-EventSubscriber -SourceIdentifier ProcessStarted Start-Process -FilePath calc Unregister-Event -SourceIdentifier ProcessStarted # ? Eigene Events auslösen Register-EngineEvent -SourceIdentifier myEvent -Action { "A L A R M!" | Write-Warning } New-Event -SourceIdentifier myEvent Unregister-Event -SourceIdentifier myEvent #endregion #region Objekt-Bindung in der Pipeline @" Benutzername;Passwort;Beschreibung p.lustig;P@ssw0rd;Peter Lustig (IT) e.gruen;Geh1imAbc;Eva Grün (HR) "@ | Set-Content -Path c:\temp\NewUsers.csv Get-Content -Path C:\temp\NewUsers.csv | ConvertFrom-Csv -Delimiter ";" -Header Name, Password, Description | Select-Object -Skip 1 -Property Name, ` Description, ` @{Label="Password"; Expression={$_.Password | ConvertTo-SecureString -AsPlainText -Force}} | New-LocalUser -WhatIf #endregion #region Splatting (Mehrere Parameter gleichzeitig an ein Cmdlet übergeben) Get-EventLog -LogName System -Newest 5 -EntryType Error, Warning # vs. $argument = @{ LogName = 'System' Newest = 5 EntryType = 'Error', 'Warning' } Get-EventLog @argument #endregion #region Output #region PowerShell # ? Formatierte Ausgabe Get-Command -Verb Format -Module Microsoft.PowerShell.* # ! Übersicht der Cmdlets Get-Process | Format-Table -Property Name, Company, CPU, TotalProcessorTime, WorkingSet64 -AutoSize # ! Tabellenausgabe Get-Process | Format-Table -Property Name, @{Label="WS (MB)"; Expression={"{0,7:0.0}" -f ($_.WorkingSet64 / 1MB) }} # ! Tabellenausgabe Get-EventLog -LogName System -Newest 10 -EntryType Error | Format-Table -Wrap # ! Tabellenausgabe Get-Process | Format-List -Property * # ! Listenausgabe Get-Alias | Sort-Object -Property Name | Format-Wide -Property Name -Column 10 # ! Komprimierte Ausgabe Get-Service -Name SsTpSvc | Format-Custom -Property Name, StartType, DependentServices -Depth 2 # ! Verschachtelte Klassen-Ausgabe # ? Div. Meldungen mit einem Status ausgeben Get-Command -Verb Write -Module Microsoft.PowerShell.Utility # ! Übersicht der Cmdlets # ! ACHTUNG: Write-Host nicht in Kombination mit PowerShell Core benutzen # ! (Write-Host ist veraltet! Nachfolger Write-Output) "Hallo Köln!" | Write-Output # ! Das Ziel kann angegeben werden z.B. Datei, etc. "Achtung ich bin ein Fehler" | Write-Error # ! Stop das Script wenn CommonParameter '-ErrorAction Stop' gesetzt Write-Warning "und ich bin eine Warnung" # ! Evtl. wird das Script gestoppt CommonParameter -WarningAction Stop Write-Debug "und ich eine Debug-Meldung" # ! CommonParameter -Debug 0..100 | ForEach-Object { Write-Progress -Activity "Verarbeite Daten" -Status "$_ Prozent" -PercentComplete $_ # ! Einen %-Fortschritt anzeigen Start-Sleep -Milliseconds 100 } # ? Die Ausgabe umleiten Get-Command -Verb Out -Module Microsoft.PowerShell.* # ! Übersicht der Cmdlets Get-ChildItem -Path c:\ -Recurse | Out-Host -Paging Get-ChildItem -Path c:\ -Directory | Select-Object -First 10 | Out-Printer # ! Ausgabe umleiten zum Druckern Get-ChildItem -Path c:\ -Directory | Select-Object -First 10 | Out-GridView # ! Ausgabe umleiten in eine Fenster Get-ChildItem -Path c:\ -Directory | Select-Object -First 10 | Out-File # ! Ausgabe umleiten in eine Datei Get-ChildItem -Path c:\ -Directory | Select-Object -First 10 | Out-String # ! Ausgabe umleiten in einen String Get-ChildItem -Path c:\ -Directory | Select-Object -First 10 | Out-Null # ! Ausgabe unterdrücken # ! Siehe auch: Send-MailMessage ; Set-Content ; Invoke-SqlCmd -Query "INSERT INTO ..." #endregion #region .NET #region eine ALARM-Meldung Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Speech $xaml = @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp2" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" WindowStyle="None" mc:Ignorable="d" SizeToContent="WidthAndHeight" Title="MainWindow"> <Grid Background="Red"> <TextBlock FontFamily="Consolas" FontSize="100" FontWeight="ExtraBold" Foreground="White" Margin="50" HorizontalAlignment="Center" VerticalAlignment="Center">A L A R M !</TextBlock> </Grid> </Window> "@ $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]$xaml) $window = [System.Windows.Markup.XamlReader]::Load($reader) $window.Topmost = $true $window.Show() | Out-Null $speaker = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer -Property @{ Rate = 2; Volume = 100 } $speaker.SpeakAsync('ALARM! ALARM! ALARM! ALARM!') Start-Sleep -Seconds 5 $window.Close() #endregion #region .NET Framework WPF 'Hallo Welt' function Invoke-WpfHalloWelt { [CmdletBinding(SupportsShouldProcess=$false)] param() 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() } Invoke-WpfHalloWelt #endregion #region Diagramm erstellen per DataVisualization using namespace System.Windows.Forms.DataVisualization.Charting using namespace System.Windows.Forms using namespace System.Drawing Add-Type -AssemblyName System.Windows.Forms.DataVisualization $chartTitle = "Top Memory Usage" $axisYTitle = "Memory (MB)" $axisXTitle = "Process Name" $dataSource = Get-Process | Sort-Object PrivateMemorySize -Descending | Select-Object -First 10 | ForEach-Object -Process { [PSCustomObject]@{ Name = $_.Name PrivateMemorySizeMB = [int]($_.PrivateMemorySize / 1MB) VirtualMemorySizeMB = [int]($_.VirtualMemorySize / 1MB) } } $chart1 = New-Object Chart $chart1.Width = 1200 $chart1.Height = 1000 $chart1.BackColor = [Color]::White $chart1.Dock = [DockStyle]::Fill $chart1.Titles.Add($chartTitle) | Out-Null $chart1.Titles[0].Font = "Arial,13pt" $chart1.Titles[0].Alignment = [ContentAlignment]::TopCenter $chartArea = New-Object ChartArea $chartArea.Name = "ChartArea1" $chartArea.AxisY.Title = $axisYTitle $chartArea.AxisX.Title = $axisXTitle $chartArea.AxisY.Interval = 100 $chartArea.AxisX.Interval = 1 $chart1.ChartAreas.Add($chartArea) $legend = New-Object 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 = [Color]::LightGreen $dataSource | ForEach-Object { $chart1.Series["VirtualMem"].Points.AddXY( $_.Name, $_.VirtualMemorySizeMB) | 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 = [Color]::Orange $dataSource | ForEach-Object { $chart1.Series["PrivateMem"].Points.AddXY( $_.Name, $_.PrivateMemorySizeMB) | Out-Null } # z.B. als PNG speichern, oder $chart1.SaveImage("c:\temp\$chartTitle.png", "png") # z.B. im Fenster anzeigen $Form = New-Object Form $Form.Width = 1300 $Form.Height = 800 $Form.StartPosition = [FormStartPosition]::CenterScreen $Form.controls.add($chart1) $Form.Add_Shown({$Form.Activate()}) $Form.ShowDialog() #endregion # ! Siehe auch: # ! Cmdlet-Grundgerüst per GUI bauen => https://poshgui.com/CmdletBuilder #endregion #endregion #region Input #region PowerShell #region Read-Host # ? Datum gem. den Ländereinstellungen abfragen $ci = New-Object -TypeName System.Globalization.CultureInfo -ArgumentList $PSCulture $benutzereingabe = Read-Host -Prompt "Bitte Datum eingeben ($($ci.DateTimeFormat.ShortDatePattern))" $benutzereingabe.ToDateTime($ci) # ? Credential verschlüsselt speichern $username = Read-Host -Prompt "Benutzername eingaben" $password = Read-Host -Prompt "Passwort für $username eingeben" -AsSecureString [PSCustomObject]@{ Username = $username Password = $password | ConvertFrom-SecureString } | ConvertTo-Json | Set-Content -Path C:\Temp\Credential_Admin.json -Force $admin = Get-Content -Path C:\Temp\Credential_Admin.json | ConvertFrom-Json | Select-Object Username, @{Label="Password"; Expression={$_.Password | ConvertTo-SecureString}} $cred = [System.Management.Automation.PSCredential]::new($admin.Username, $admin.Password) Enter-PSSession -ComputerName $ZielComputer -Credential $cred #endregion #region Out-GridView / Show-Command # ? Eine InputBox simulieren $antwort = "JA, Dateien archivieren", "NEIN, Dateien am Ort belassen" | Out-GridView -OutputMode Single $antwort.StartsWith('NEIN') # ? Ein Cmdlet per GUI mit dem Benutzer interagieren lassen Import-Module -Name .\Modules\AKPT Get-EuroExchange -ListCurrency | Out-GridView -OutputMode Multiple | Get-EuroExchange | Out-GridView #endregion #region PromptForChoice $titel = "Aktion wählen" $nachricht = "Wie lautet Ihre Entscheidung?" $ja = New-Object System.Management.Automation.Host.ChoiceDescription "&Ja" , "Hilfetext zu JA" $nein = New-Object System.Management.Automation.Host.ChoiceDescription "&Nein" , "Hilfetext zu NEIN" $vielleicht = New-Object System.Management.Automation.Host.ChoiceDescription "&Vielleicht", "Hilfetext zu VIELLEICHT" $optionen = [System.Management.Automation.Host.ChoiceDescription[]]($ja, $nein, $vielleicht) $ergebnis = $host.UI.PromptForChoice($titel, $nachricht, $optionen, 1) switch ($ergebnis) { 0 {"Die Auswahl ist JA"} 1 {"Die Auswahl ist NEIN"} 2 {"Die Auswahl ist VIELLEICHT"} } #endregion #endregion #region .NET #region MessageBox using namespace System.Windows.Forms # Betr. Namespace bekannt machen; MUSS IN DEN ERSTEN ZEILEN STEHEN [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null # Betr. DLL-Datei wird geladen $title = "Dateien archivieren" $message = "Alte Dateien archivieren? (Werden am Ursprungsort gelöscht auf ... kopiert)" $buttons = [MessageBoxButtons]::YesNo $icon = [MessageBoxIcon]::Question $defaultButton = [MessageBoxDefaultButton]::Button1 $antwort = [MessageBox]::Show($message, $title, $buttons, $icon, $defaultButton) if($antwort -eq [DialogResult]::Yes) { "OK, die Dateien werden archiviert..." } #endregion #region Common Dialogs # ? OpenFileDialog using namespace System.Windows.Forms # Betr. Namespace bekannt machen; MUSS IN DEN ERSTEN ZEILEN STEHEN [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null # Betr. DLL-Datei wird geladen $ofd = New-Object OpenFileDialog $ofd.Filter = "PowerShell-Skripten (*.ps1)|*.ps1|Text-Dateien (*.txt)|*.txt|Alle Dateien (*.*)|*.*" $ofd.InitialDirectory = "C:\Temp" $dialogResult = $ofd.ShowDialog() if ($dialogResult -eq [DialogResult]::OK) { $ofd.Filename } # ? FolderBrowserDialog using namespace System.Windows.Forms # Betr. Namespace bekannt machen; MUSS IN DEN ERSTEN ZEILEN STEHEN [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null # Betr. DLL-Datei wird geladen $fbd = New-Object FolderBrowserDialog $fbd.Description = "Bitte Ordner für das Archivieren auswählen" $fbd.RootFolder = [Environment+SpecialFolder]::Desktop $fbd.ShowNewFolderButton = $true $dialogResult = $fbd.ShowDialog() if ($dialogResult -eq [DialogResult]::OK) { $fbd.SelectedPath } #endregion #region WPF 'Euro Rechner' # Betr. Namespace bekannt machen; MUSS IN DATEIKOPF using namespace System.PresentationFramework using namespace System.Windows.Markup using namespace System.Xml Add-Type -AssemblyName PresentationFramework $url = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml" $xml = [xml](Invoke-WebRequest -Uri $url | Select-Object -exp Content) $cubes = $xml.Envelope.Cube.Cube.Cube $currencys = $cubes | Sort-Object currency | Select-Object -exp currency function EuroRateRechnen() { try { $aktWährung = $WährungenCtrl.SelectedItem [decimal]$rate = $cubes | Where-Object currency -eq $aktWährung | Select-Object -exp rate $euro = $eurosCtrl.Text $ergebnis = $rate * $euro } catch { $rate = 0 $ergebnis = 0 } $RateCtrl.Text = "{0:#,##0.0000} {1}" -f $rate , $WährungenCtrl.SelectedItem $ErgebnisCtrl.Text = "{0:#,##0.0000} {1}" -f $ergebnis, $WährungenCtrl.SelectedItem } [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="280" WindowStartupLocation="CenterScreen"> <Grid Margin="20"> <StackPanel> <Label Margin="-4 0 0 -2">Währungssymbol</Label> <ComboBox x:Name="Währungen" /> <Label Margin="-4 8 0 -2">Rate</Label> <TextBox x:Name="Rate" IsReadOnly="True" TextAlignment="Right" /> <Label Margin="-4 8 0 -2">€ (Euros)</Label> <TextBox x:Name="Euros" TextAlignment="Right" /> <Label Margin="-4 8 0 -2">Ergebnis</Label> <TextBox x:Name="Ergebnis" IsReadOnly="True" TextAlignment="Right" FontWeight="Bold" /> </StackPanel> </Grid> </Window> "@ $reader = New-Object System.Xml.XmlNodeReader $xaml $window = [System.Windows.Markup.XamlReader]::Load($Reader) $WährungenCtrl = $window.FindName('Währungen') $RateCtrl = $window.FindName('Rate') $EurosCtrl = $window.FindName('Euros') $ErgebnisCtrl = $window.FindName('Ergebnis') $currencys | ForEach-Object { $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 #region Read-Window Import-Module .\Modules\AKPT Read-Window -Title "Alte Dateien finden" -Message "Alte Dateien sind älter als?" -Default "01.01.2010" #endregion # ! Siehe auch: # ! Cmdlet-Grundgerüst per GUI bauen => https://poshgui.com/CmdletBuilder #endregion #endregion #region Module # ? Überblick Get-Module -Name * -ListAvailable # ! Eine Übersicht aller INSTALLIERTEN Module, gruppiert nach Modul-Ordner Get-Module -Name * # ! Welche Module sind z.Zt. importiert/geladen # ? Modul installieren $env:PSModulePath -split ";" # ! Installationsorte Get-ChildItem -Path C:\Windows\system32\WindowsPowerShell\v1.0\Modules # ! Das Modul steht computerweit zur Verfügung, wenn es in dem Default-Ordner liegt: Get-ChildItem -Path C:\Users\Alex\Documents\WindowsPowerShell\Modules # ! Das Modul steht dem lokalen Benutzer zur Verfügung, wenn es in dem Default-Ordner liegt: Copy-Item -Path .\AKPT -Destination C:\Users\Administrator\Documents\WindowsPowerShell\Modules\AKPT\ -Force -Recurse # ! Module per Copy installieren Install-Module -Repository PSGallery -Name AKPT -SkipPublisherCheck -Scope CurrentUser -Force # ! Module per PowerShellGallery installieren # ? Modul IMPORTIEREN und dadurch in der Session nutzbar machen Import-Module -Name .\Modules\AKPT -Verbose # ! Modul AKPT laden/importieren soweit es den installiert wurde Get-Hotkeys # ! ... Seit der PowerShell 3.0 wird Import-Module automatisch ausgeführt Get-Help -Name Get-Hotkeys -ShowWindow # ! ... wenn die Hilfe zu einem Cmdlet aufgerufen wird Get-Command -Module AKPT # ! ... wenn der Inhalt des Moduls angezeigt wird # ? Ein Modul manuell entladen z.b. für das Debugging nötig Remove-Module -Name AKPT -Force # ? Modul deinstallieren Uninstall-Module -Name AKPT -AllVersions -Force #region Ein Module von PowerShellGallery downloaden, analysieren und testhalber importieren Find-Module -Tag AttilaKrick Find-Module -Name AKPT | Format-List -Property Name, Version, Description, Author, PublishedDate, AdditionalMetadata, ReleaseNotes # TODO Übung AdditionalMetadata lesbar machen Save-Module -Name 'AKPT' -Path "$env:USERPROFILE\Downloads" -Force -Verbose # -AcceptLicense # ! Ein Module vom FileSystem laden Import-Module -Name "$env:USERPROFILE\Downloads\AKPT\*\AKPT.psd1" -Verbose Install-Module -Name AKPT -Scope CurrentUser -Verbose Update-Module -Name AKPT -Verbose #endregion #endregion #region PSProvider & PSDrive Get-Command -Noun ChildItem, Item, ItemProperty, ItemPropertyValue, Location, Path -Module Microsoft.PowerShell.* # ! Übersicht aller Default-Cmdlets Get-PSProvider # ! Übersicht z.Zt. geladener PSProvider Get-PSDrive # ! Übersicht z.Zt. geladene PSDrives Import-Module -Name SqlServer # ! Durch das importieren eines Modules können weitere PS-Provider aktiviert werden # ? Beispiel 1 New-PSDrive -PSProvider FileSystem -Name DL -Root $env:USERPROFILE\Downloads Get-ChildItem -Path DL:\ New-PSDrive -PSProvider Registry -Name HKCC -Root HKEY_CURRENT_CONFIG Get-ChildItem -Path HKCC: # ? Beispiel 2 New-Item -Path C:\Temp -Name NeueDatei.txt -ItemType Directory New-Item -Path HKCU:\Software -Name NeuerKey -ItemType Key New-Item -Path variable:\ -Name NeueVariable -Value "Hallo Köln!" -ItemType Variable # ! $NeueVariable = "Hallo Köln!" ; New-Variable -Name NeueVariable -Value "Hallo Köln!" -Option ReadOnly New-ItemProperty -Path HKCU:\SOFTWARE\NeuerKey -Name Heute -Value (Get-Date) -PropertyType String # ? Absolute Pfad-Angaben Get-ChildItem -Path c:\ Get-ChildItem -Path variable:\ # $PsUiCulture Get-ChildItem -Path cert:\ Get-ChildItem -Path env:\ # $env:WinDir Get-ChildItem -Path hkcu:\ Get-ChildItem -Path SqlServer:\ # ? Relative Pfad-Angaben Get-ChildItem -Path . # . => Aktueller Ordner Get-ChildItem -Path .. # .. => Übergeordneter Get-ChildItem -Path \ # \ => Stammordner Get-ChildItem -Path ~ # ~ => Home-Verzeichnis # ? Handling mit den Laufwerken Set-Location -Path HKCU:\SOFTWARE # ! Aktuellen Ordner setzen Set-Location -Path C:\Windows\System32 # ! Aktuellen Ordner setzen Resolve-Path -Path .\MsInfo32.exe # ! Pfad auflösen Test-Path -Path C:\Windows\System32\MsInfo32.exe # ! Pfad testen (Nur Syntax-Prüfung mit: -IsValid) Test-Path -Path Variable:\PSUICulture # ! Pfad testen Split-Path -Path C:\Windows\System32\MsInfo32.exe -Parent # ! Pfad zerlegen Split-Path -Path C:\Windows\System32\MsInfo32.exe -Leaf # ! Pfad zerlegen Join-Path -Path $env:WinDir -ChildPath System32 # ! Pfad zusammenbauen #endregion #region Remoting # ? PowerShell-Remoting (WinRM) einrichten Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private -PassThru Enable-PSRemoting -Force Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value * -Force # ? Remoting nutzen: Simple Enter-PSSession -ComputerName "192.168.0.100" New-Item -Path C:\temp\AkWasHere.V1 -ItemType File Exit-PSSession # ? Remoting nutzen: Complex $admin = Get-Credential -Message "Anmeldung für Server X & Y" -UserName Administrator $serverX = New-PSSession -ComputerName "192.168.0.101" -Credential $admin -Name X $serverY = New-PSSession -ComputerName "192.168.0.102" -Credential $admin -Name Y Enter-PSSession -Session $serverX Exit-PSSession Enter-PSSession -Session $serverY Exit-PSSession Remove-PSSession -Session $serverX, $serverY Get-PSSession # ? Remoting nutzen: 1:n Invoke-Command -ComputerName "192.168.0.103", "192.168.0.104" -ScriptBlock { Get-Process } -AsJob -JobName lfdProcXY Get-Job Receive-Job -Name lfdProcXY -Keep # ! Siehe auch: # ! Remoting Troubleshooting => B09_Remoting.ps1 #endregion #region Operatoren # ? Übersicht aller Operatoren Get-Help -Name about_Operators -ShowWindow # ! Übersicht aller Operatoren Get-Help -Name about_Operator_Precedence -ShowWindow # ! Vorrangregeln Get-Help -Name about_Arithmetic_Operators -ShowWindow # ! Arithmetische- Operatoren Get-Help -Name about_Assignment_Operators -ShowWindow # ! Zuweisungs- Operatoren Get-Help -Name about_Logical_Operators -ShowWindow # ! Logische- Operatoren Get-Help -Name about_Comparison_Operators -ShowWindow # ! Vergleichs- Operatoren Get-Help -Name about_Type_Operators -ShowWindow # ! Typen- Operatoren Get-Help -Name about_Split -ShowWindow # ! String-Zerlege- Operatoren Get-Help -Name about_Join -ShowWindow # ! String-Zusammenfüge-Operatoren # ? Beispiele zu: about_Arithmetic_Operators 1 + 1 ; 2 * 10 ; 9 % 2 ; 15 -shl 1 # ? Beispiele zu: about_Assignment_Operators $zahl=1 ; $zahl++ ; $zahl+=2 $varB = @() ; $varB += (Get-Process)[0] ; $varB += (Get-Process)[1] # ? Beispiele zu: about_Logical_Operators $true -and $true ; !$true -and $true ; -not $true -and $true # ? Beispiele zu: about_Comparison_Operators "Köln" -eq "köln" ; "Köln" -ceq "köln" ; "Köln" -ieq "köln" "Hallo Köln!" -iReplace "Köln", "Berlin" "Köln", "Hamburg", "München" -contains "Köln" "abc@abc.de" -like "*@*.*" "AB678.docx" -match "^[A-Z]{2,2}[0-9]{3,3}\.doc[x]{0,1}$" 10,11,12,13,14,10 -ne 10 # ? Beispiele zu: about_Type_Operators (Get-Process) -is [System.Array] "2015-03-18" -is [DateTime] "2015-03-18".PSTypeNames # ? Beispiele zu: -f (Format-Operator) s. about_operators "Wert 1 ist {0}, der letzte Wert ist {3} und die anderen sind: {1}, {2}" -f 10,11,99,230,1024 "Zahlenformat: {0:#,##0.00}" -f 123456.6789 "Zahlenformat: {0:c2}" -f 123456.6789 "Log_{0:yyyyMMddHH}.log" -f (Get-date) # ? Beispiele zu: about_Split, about_Join, "ADS001;127.0.0.1;1234.67;" -split ";" ("Hallo Köln!", "127.0.0.1", 123.45) -join ";" # ? Beispiele zu: Spezial-Operator $c = {Get-Service -Name Spooler} ; & $c . c:\scripts\sample.ps1 [datetime]$birthday = "1/20/88" ; $birthday 1..10 [datetime]::now #endregion #region Typen und Variable # ? String $ort = "Köln" "Hallo $ort!" 'Hallo $ort!' # ? Ganzzahlen (Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64) "{0:x}" -f 255 0xFF [Convert]::ToByte(0xFF) # ? DateTime, DateTimeOffset [Convert]::ToDateTime("1.10.2019") [Convert]::ToDateTime("10/1/2019", [CultureInfo]::new("en-US")) # ? Unix-Time $unixtime = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion" | Select-Object -ExpandProperty InstallDate $span = New-TimeSpan -Seconds $unixtime $start = [datetime]"1970-01-01" $start + $span [System.NET.Mail.MailAddress]'Attila Krick<a.krick@outlook.com>' # ? TimeSpan (Zeitdauer) [TimeSpan]1 [timespan]5d New-TimeSpan -Minutes 1 # ? Array: Eindimensional $array1 = 1, 2, 3, 4 $array1 = 1..4 $array1 = Get-Process $array1[2] # ? Array: Zweidimensionale $array2 = (11,12,13), (21,22,23), (31,32,33) $array2[1][1] $array2[$array2.Length-1] # ? Array: Gemischte $array3 = "Hallo", 12, (Get-Process) $array3.Length $array3 | Select-Object -Last 1 # ? Array: Ein Multidimensionales $arrayMD = New-Object -TypeName ‘Int32[, ]’ -ArgumentList 2, 2 # ? Hashtable @{ HashNameA="Wert"; HashNameB="Wert"; } # ! Syntax Get-ChildItem -Path c:\temp -File | Select-Object -Property Name, @{Label="LengthKB"; Expression={$_.Length / 1KB}} Get-ChildItem -Path c:\temp -File | ForEach-Object -Process { [PSCustomObject]@{ Dateiname = $_.Name DateigrößeKB = [int]($_.Length / 1KB) Besitzer = $_ | Get-Acl | Select-Object -ExpandProperty Owner } } # ? Ein Hashtable durch Benutzung erstellen $person = @{} $person.Age = 23 $person.Name = ‘Tobias’ $person.Status = ‘Online’ $person.Name $person # ? StringData $LocalizedData = Data { ConvertFrom-StringData @' CannotDetermineTestName = Cannot determine test name InvalidTestInfo = TestInfo must contain the path and the list of tests InspectingModules = Inspecting Modules DiagnosticSearch = Searching for Diagnostics in {0} InvokingTest = Invoking tests in {0} '@ } $LocalizedData.InvalidTestInfo $LocalizedData.InvokingTest # ? ScriptBlock $sc = {Get-Process} ; . $sc # ? Die wichtigsten PowerShell Typen [PSObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Get.Keys | Sort-Object | ForEach-Object { “[$_]” } # ? Eigenen Datentypen definieren class Wechselkurs { [ValidatePattern('^[A-Z]{3,3}$')] [string]$Währung [ValidateRange(0, 10000)] [decimal]$Rate } New-Object -TypeName Wechselkurs -Property @{Währung = 'USD'; Rate = 1.4567} # ? Werttypen (Primitive Typen) $a = 10 $b = 20 $a = $b $b = 99 '$a = {0} ; $b = {1}' -f $a, $b [Object]::ReferenceEquals($a, $b) $b.GetType().IsPrimitive # ? Referenztype $a = @(10) $b = @(20) $a = $b $b[0] = 99 '$a = {0} ; $b = {1}' -f $a[0], $b[0] [Object]::ReferenceEquals($a, $b) $b.GetType().IsPrimitive # ? Variable: Gültigkeitsbereiche $Global:A # Auf Ebene der PowerShell-Anwendung $Script:B # Auf Ebene des Scripts/Modules $Private:C # Auf Ebene des aktuellen Gültigkeitsbereiches # ? Call-Operator . und & Set-Location -Path C:\Temp '$nachricht = "Message from Test.ps1"' | Set-Content -Path .\Test.ps1 -Force $nachricht = "Keine Nachricht!" . .\Test.ps1 # ! Aufruf im aktuellen Gültigkeitsbereich "Die akt. Nachricht lautet: $nachricht" $nachricht = "Keine Nachricht!" & .\Test.ps1 # ! Aufruf im eigenen Gültigkeitsbereich "Die akt. Nachricht lautet: $nachricht" # ? PowerShell-Provider für Variable Get-ChildItem -Path Variable: -Force Get-Item -Path Variable:\PSVersionTable Set-Item -Path Variable:\PSUICulture -Value "en-US" -Force Test-Path -Path Variable:\Error # ? Variable erstellen $planet = "Erde" New-Variable -Name $planet -Value "Mars" -Force # ! FALSCH New-Variable -Name planet -Value "Mars" -Force # * RICHTIG Get-ChildItem -Path C:\ -Force -Recurse -File -ErrorVariable sumGciErrors -ErrorAction SilentlyContinue New-Variable -Name PI -Value 3.14 -Option ReadOnly $PI = 99 New-Variable -Name PI2 -Value 3.14 -Option Constant $PI2 = 99 Set-Variable -Name PI -Value 99 -Force Set-Variable -Name PI2 -Value 99 -Force # ? Typen-Umwandlung vs. Variablentyp festlegen $b = [int]"99" # Der Typ von $b kann sich ändern [int]$c = "99" # $c wird für IMMER auf int festgelegt $b | Get-Member $c | Get-Member $b = "Hallo Welt!" $b | Get-Member $c = "Hallo Welt!" # ? Variable: Sonderformen $env:WinDir # Windows Umgebungsvariablen ${c:\temp\JetztIst.txt} = Get-Date # Datei als Variable-Speicher # ? Prüfen ob eine Variable (PSProvider) vorhanden ist Test-Path -Path variable:var1 Test-Path -Path variable:varX # ! .PS1-Empfehlung => Variablen müssen vor dem Benutzen deklariert werden Set-StrictMode -Version Latest $password = "geheim" $pasword -eq "geheim" # ? Variablen evtl. in Skripten und Funktionen aufräumen Remove-Variable -Name alter Remove-Variable -Name PI -Force Remove-Variable -Name * -Force -ErrorAction SilentlyContinue #endregion #region PowerShell-Dateien / -Dateierweiterungen # ! .ps1 (PowerShell Script version 1.0) # Ausführbare Skriptdatei (Beachte Ausführungsrichtlinien= # ! .ps1xml # PowerShell Format- und Typdefinitionen-Datei Get-ChildItem -Path C:\Windows\System32\WindowsPowerShell\v1.0\*.ps1xml # !.psc1 (PowerShell Console version 1) Export-Console -Path c:\temp\myconsole.psc1 powershell.exe -PSConsoleFile c:\temp\myconsole.psc1 # ! .psd1 (PowerShell Definition version 1) # Modul-Manifest-Datei # Definiert die Eckdaten eines Modules und psdm1 den Inhalt des Modules, z.B. siehe: Get-ChildItem -Path C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive # ! .psm1 (PowerShell Modul version 1) # Ausführbare Modul-Skriptdatei die beim importieren eines Moduls ausgeführt wird) # Beachte Ausführungsrichtlinien #endregion #region PowerShell-Dateien-Ausführungsrichtlinien Get-ExecutionPolicy -List # ! Was ist eingestellt $env:PSExecutionPolicyPreference # ! Was gilt für den aktuellen Scripthost Set-ExecutionPolicy -ExecutionPolicy AllSigned # ! Empfohlen Einstellung für Client und Server Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser # ! Empfohlen Einstellung für Test-/Entwicklungszwecke (Remote-Scripte müssen signiert sein) #endregion #region Kontrollstrukturen # TODO Für die korrekte Syntax gibt es in Visual Studio Code Schnipsel zum einfügen (CTRL + ALT + J) oder über Autovervollständigung (Beachte Symbol) Get-Help -Name about_if -ShowWindow Get-Help -Name about_switch -ShowWindow Get-Help -Name about_while -ShowWindow Get-Help -Name about_do -ShowWindow Get-Help -Name about_do -ShowWindow Get-Help -Name about_for -ShowWindow Get-Help -Name about_foreach -ShowWindow Get-Help -Name about_Break -ShowWindow Get-Help -Name about_Continue -ShowWindow #endregion #region Debugging (Visual Studio Code) # ? VSCode-Debug-Modus-Tastaturbefehle # ! F9 => Haltepunkt setzen/lösen # ! F5 => Debug-Modus starten, die Ausführung bleibt beim ersten Haltepunkt stehen # ! F10 => Funktionsaufrufe werden übersprungen und im gesamt abgearbeitet # ! F11 => Führt die nächste Code-Zeile aus (Gelbe Zeile => wurde NOCH NICHT ausgeführt) # ! Ein erreichbarer Funktionsaufrufe wird angesprungen # ! SHIFT + F5 => Beenden den Debug-Modus vorzeitig # ? In den Debug-Modus wechseln wenn sich Variablen ändern (dynamische Haltepunkte) Set-PSBreakpoint -Variable ipId -Mode Write # ! Anhalten wenn die Variable $ipId beschieben wird: Set-PSBreakpoint -Variable ipAddress -Mode Write -Action { if ($ipAddress -ceq "192.168.178.3") {break} } # ! Anhalten wenn die Variable den Wert "192.168.178.5" enthält: # ? Debug-Meldungen ausgeben $DebugPreference = [System.Management.Automation.ActionPreference]::Continue # ! Script-Lokal aktivieren Test-NetConnection -ComputerName 127.0.0.1 -Debug # ! oder Cmdlet lokal aktivieren # ? Tracing Trace-Command -Name ParameterBinding -Expression { Get-Process spoolsv } -PSHost # ! Cmdlet-Verarbeitung analysieren Get-TraceSource | Select-Object -Property Name, Description | Out-GridView # ! Welche Überwachungen es gibt liefert folgendes Cmdlet # ? Der Common-Parameter Verbose Remove-Module -Name AKPT -Verbose Import-Module -Name .\Modules\AKPT -Verbose # ? Die Dauer einer Ausführung messen Measure-Command -Expression { 1..5 | ForEach-Object -Process { "Verarbeite Nummer $_"; Start-Sleep -Seconds 1 } | Out-Default } 1..5 | Measure-Command -Expression { "Verarbeite Nummer $_" | Out-Default; Start-Sleep -Seconds 1 } #endregion #region Error Handling #region Meldungen unterdrücken Get-Process -FileVersionInfo -ErrorAction Ignore -WarningAction Ignore -InformationAction Continue -Verbose # ! Lokal Unterdrückung $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ! Globale Unterdrückung, Default => Continue $WarningPreference = [System.Management.Automation.ActionPreference]::Stop # ! Globale Unterdrückung, Default => Continue $InformationPreference = [System.Management.Automation.ActionPreference]::Stop # ! Globale Unterdrückung, Default => SilentlyContinue $VerbosePreference = [System.Management.Automation.ActionPreference]::Stop # ! Globale Unterdrückung, Default => SilentlyContinue # ? Meldungen in eigenen Cmdlets einbauen function Test-CommonParameter { [CmdletBinding()] # ! WICHTIG um die Common-Parameters nutzen zu können param ( ) "Error Test" | Write-Error "Warning Test" | Write-Warning "Verbose test" | Write-Verbose Write-Information -MessageData "Information Test" "Debug Test" | Write-Debug } Test-CommonParameter Test-CommonParameter -ErrorAction SilentlyContinue -WarningAction Ignore -Verbose -InformationAction Continue Test-CommonParameter -Debug #endregion #region Fehler analysieren # ! Hilfreiches Cmdlet zur Analyse von Fehler: filter Get-ErrorDetails { $Category = @{ Name = 'CategoryInfo' ; Expression = { $_.CategoryInfo.Category }} $Line = @{ Name = 'LineNumber' ; Expression = { $_.InvocationInfo.ScriptLineNumber }} $Script = @{ Name = 'ScriptName' ; Expression = { $_.InvocationInfo.ScriptName }} $Target = @{ Name = 'Target' ; Expression = { $_.TargetObject }} $ErrorId = @{ Name = 'ErrorId' ; Expression = { $_.FullyQualifiedErrorID }} $ExType = @{ Name = 'ExceptionType' ; Expression = { $_.Exception.GetType().FullName }} $ExMessage = @{ Name = 'ExceptionMessage' ; Expression = { $_.Exception.Message }} $ExInnerException = @{ Name = 'ExceptionMessage' ; Expression = { $_.Exception.InnerException }} $Input | Select-Object -Property $Category, $Line, $Script, $Target, $ErrorId, $ExType, $ExMessage, $ExInnerException } # ? Beispiel: $myErrors = @() Get-Process -FileVersionInfo -ErrorVariable myErrors # ! Löst Fehler aus Get-Content -Path C:\MichGibtEsNicht.txt -ErrorVariable myErrors # ! Löst Fehler aus $myErrors | Get-ErrorDetails #region Die $error-Systemvariable $error # Enthält 256 Fehler seit beginn der Session $MaximumErrorCount $MaximumErrorCount = 999 # Default 256 $error.Count $error | Select-Object -Last 1 # ! Der älteste Fehler $error | Select-Object -First 1 # ! Der neuste Fehler ($error[0]) # ? Nutzen, z.B.: $error.Clear() 42 / 0 "Aufgetretene Fehler: $($error.Count)" $error | Get-ErrorDetails #endregion # ? Wurde der letzte Befehl erfolgreich ausgeführt: Stop-Service -Name Audiosrv -Force if ($?) { "'Stop-Service -Name Audiosrv -Force' wurde erfolgreich ausgeführt!" } #endregion #region Fehler behandeln #region per try, catch und finally # ! try..catch nur benutzen wenn EINE LÖSUNG implentiert wird, # ! da dieses Konstrukt der PowerShell signalisiert das der Fehler behoben wurde! # ! ####################################### # ! ### EINE MELDUNG IST KEINE LÖSUNGEN ### # ! ####################################### try { $ErrorActionPreferenceBackup = $ErrorActionPreference $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ! Code der evtl. einen Fehler auslöst: Get-Content c:\temp\wichtig.txt } catch [System.Management.Automation.ItemNotFoundException] { # ! Code der diesen Fehler behebt: New-Item c:\temp\wichtig.txt } catch [System.OutOfMemoryException] { # ! Völlig spezifischer Fehler # ! Code der diesen Fehler behebt } catch [System.SystemException] { # Bereichsfehler } catch { # Völlig unspezifische Fehler # ! Code der diesen Fehler behebt => Was könnte das nur sein !?!?!? $_.GetType().FullName throw $_ } finally { $ErrorActionPreference = $ErrorActionPreferenceBackup } #endregion #region über trap $ErrorActionPreferenceBackup = $ErrorActionPreference $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop trap [System.DivideByZeroException] { '[SIRENE] => Division durch 0 wurde festgestellt' continue } trap [System.Management.Automation.ActionPreferenceStopException] { '[SIRENE] => Eine ActionPreferenceStopException ist aufgetreten, tatsächliche Exception auslösen und abfangen' throw $_.Exception continue trap [System.Management.Automation.ItemNotFoundException] { '[SIRENE] => Die Datei wurde nicht gefunden' continue } trap { '[SIRENE] => Irgend ein anderer Fehler in ActionPreferenceStopException ist aufgetreten' continue } } trap { '[SIRENE] => Irgend ein anderer Fehler ist aufgetreten' continue } 12/0 Get-Content -Path C:\MichGibtEsNicht.txt Get-Process -Name wininit -FileVersionInfo $ErrorActionPreference = $ErrorActionPreferenceBackup #endregion #endregion #region Eigene Fehler auslösen [byte]$hubraum = 5000 # ! ... durch Typenfestlegung "UPS, da hat etwas nicht geklappt" | Write-Error # ! ... durch Write-Error [ValidateRange(5, 10)]$Hubraum # ! ... durch div. Validate-Attributen für eigen Cmdlet-Parameter throw "Geburtsdatum muss <= Heute $(Get-Date -Format dd.MM.yyyy) sein" # ! ... durch throw String-Text throw New-Object -TypeName System.ArgumentException ` -ArgumentList "Wert 'xyz' ist ungültig.", "Geburtsdatum" # ! ... durch das Auslösen einer vorhanden Exception-Klasse # ! ... durch das Auslösen einer selbst deklarierte Exception-Klasse [System.SerializableAttribute()] class KundeVorhandenException : System.Exception { [string]$Message [int]$KundeId KundeVorhandenException([string]$message, [int]$kundeId) : base("$message (Kunde $kundeId)") { $this.Message = $message } } throw New-Object -TypeName KundeVorhandenException -ArgumentList "Der Kunde ist bereits vorhanden", 4711 #endregion #endregion #region Muster-Aufbau von ausführbaren Dateien (.PS1 & .PSM1) # ! 1. Übersicht der benutzten Module (d.h. evtl. vorher installieren) using Module Microsoft.PowerShell.Management using Module Microsoft.PowerShell.Utility # ! 2. using-Anweisung zum Verkürzen, z.B. [System.Management.Automation.ActionPreference]::Stop auf [ActionPreference]::Stop using namespace System.Management.Automation using namespace System.Windows.Forms # ! 3. Beim auftreten eines Fehler => Script-Ausführung abbrechen $ErrorActionPreference = [ActionPreference]::Stop # Default ist 'Continue' # ! 4. Script-Übergabe-Parameter definieren param ([string]$Path, [int]$BenutzerId) # ? Aufruf: MeinScript.ps1 -Path c:\temp -BenutzerId 47110815 # ! 5. Benötigte .NET Assembly laden Add-Type -AssemblyName System.Windows.Forms # ! 6. Sicherstellen das Variablen vor der ersten Verwendung deklariert wurden Set-StrictMode -Version Latest # ! 7. Vor der ersten Verwendung von Functions die Function-Definitionen importieren. function Invoke-Irgendwas { param ([string]$Stadtname) "Stadtname: $Stadtname" } # ! 8. Der eigentliche Logik-Code des Skriptes: $title = Invoke-Irgendwas -Stadtname "Köln" $fenster = New-Object -TypeName Form $fenster.Text = $title $fenster.ShowDialog() # ! [9. Digitale Signatur] #endregion #region Benutzerdefinierte Cmdlets schreiben # TODO Weiterführende und Nachschlage-Informationen Get-Content -Path .\AKPT\Wissen\C07_UserCmdlets.ps1 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 anderer Programmierer Get-ChildItem Function:\New-Guid | Select-Object -ExpandProperty ScriptBlock Install-Module -Name .\AKPT ; Get-ChildItem Function:\* | Where-Object -Property ModuleName -IEQ -Value AKPT #endregion #region Scriptdateien signieren # ! 1. Ausführungsrichtlinien auf 'AllSigned' setzen: Set-ExecutionPolicy -ExecutionPolicy AllSigned -Scope Process -Force Set-ExecutionPolicy -ExecutionPolicy AllSigned -Scope CurrentUser -Force Set-ExecutionPolicy -ExecutionPolicy AllSigned -Scope LocalMachine -Force # ! 2. Signierer-Zertifikate erstellen (Oder über PKI-Admin): $myPfxCert = New-SelfSignedCertificate -Subject "CN=_FirstName_LastName (PS Developer), E=v.nachname@abc.local" ` -HashAlgorithm SHA512 ` -KeyExportPolicy ExportableEncrypted ` -CertStoreLocation Cert:\CurrentUser\My ` -Type CodeSigningCert ` -NotAfter (Get-Date).AddYears(5) # ! 3. Signierer-Zertifikate mit dem privaten Schlüssel exportieren $myPfxCertPassword = Read-Host -Prompt "Das Passwort zum schützen des privaten Schlüssels angeben" -AsSecureString $myPfxCert | Export-PfxCertificate -Password $myPfxCertPassword -FilePath .\MyCodeSigningCert.pfx # ! 4. Aus dem Signierer-Zertifikate ein öffentliches Zertifikat erstellen: Get-PfxCertificate -FilePath .\MyCodeSigningCert.pfx | Export-Certificate -FilePath .\PublicSignerCertificate.cer -Type CERT -Force # ! 5. Vertrauensstellung einrichten: Import-Certificate -FilePath .\PublicSignerCertificate.cer -CertStoreLocation Cert:\LocalMachine\Root Import-Certificate -FilePath .\PublicSignerCertificate.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher # ! 6. .PS1-Datei signieren: $cert = Get-ChildItem -Path .\*.pfx | Out-GridView -Title "Zertifikat wählen" -OutputMode Single | Get-PfxCertificate Get-ChildItem -Path . | Where-Object Extension -In '.PS1', '.PSM1' | Out-GridView -Title "Zu signierende .PS1 oder PSM1-Datei(en) auswählen" -OutputMode Multiple | Set-AuthenticodeSignature -Certificate $cert ` -HashAlgorithm SHA512 ` -TimestampServer http://timestamp.globalsign.com/scripts/timstamp.dll # ! Die digitale Signatur prüfen Get-AuthenticodeSignature -FilePath '.\Test.ps1' | Format-List -Property * #endregion # TODO C09 # TODO C10 # TODO C11 # TODO C12 |