Check-in [06df8ad9bd]
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Derived from current base version 0.8.0; some sample and generic scripts added. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
06df8ad9bd4a649fdd91da4fd6dba04b |
User & Date: | mario 2017-09-22 23:34:42 |
2017-09-22
| ||
23:40 | Minor text fixes. check-in: 15d9b1576a user: mario tags: trunk | |
23:34 | Derived from current base version 0.8.0; some sample and generic scripts added. check-in: 06df8ad9bd user: mario tags: trunk | |
22:52 | initial empty check-in check-in: 7e2f1fd24a user: mario tags: trunk | |
Added ClickyColoury.cmd.
> > | 1 2 | @echo off powershell.exe -Version 2.0 -STA -WindowStyle Hidden -File modules/starter.ps1 |
Added TextyTypey.cmd.
> > > | 1 2 3 | @echo off rem -- This runs in PS5.0 already powershell.exe -STA -File modules/starter.ps1 -CLI |
Added UserTools/reddit.url.
> > | 1 2 | [InternetShortcut] URL=http://reddit.com/ |
Added data/UserTools.description.csv.
> > | 1 2 | file,icon,desc reddit.url,cmd.png,Reddit homepage |
Added data/combobox.automap.txt.
> > | 1 2 | $true $false |
Added data/combobox.exchangeserver.txt.
> > | 1 2 | YOURSMTP01 YOURSMTP03 |
Added data/combobox.mailperm.txt.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | FullAccess Send-as ExternalAccount DeleteItem ReadPermission ChangePermission ChangeOwner ###--for-rooms--### Owner Author Contributor PublishingEditor PublishingAuthor Reviewer None CreateItems CreateSubfolders EditAllItems FolderContact FolderOwner |
Added data/combobox.minicmdlets.txt.
> > > | 1 2 3 | Get-ADDomain Get-ADDefaultDomainPasswordPolicy [windows.forms.clipboard]::GetDataObject().getData("HTML Format") |
Added data/servers.sample.csv.
> > > | 1 2 3 | Hostname,Owner,Description localhost,Emily Examplary,well it's your host server1,Emily Examplary,some server perhaps |
Added dev/create-psd1.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # api: ps # type: cli # title: create .psd1 # description: module manifest from script meta header # version: 0.1 # # Creates a .psd1 file for a .psm1/.ps1 [CmdletBinding()] Param($fn=(ReadHost ".psm1 filename:")); $target = $fn -replace "\.\w+$",".psd1" $base = $fn -replace "^.+[\\\\/]","" # plugin meta block $src = (Get-Content $fn | Out-String) $meta = @{} $src | Select-String "(?m)^#\s*(\w+):\s*([^\n]+)" -AllMatches | % { $_.Matches } | % { ,($_.Groups | % { $_.Value }) } | % { $meta[$_[1]] = $_[2] } # create .psd1 file $psd = @{ Path = $target RootModule = $base ModuleToProcess = $base Author = $meta.author CompanyName = "-" Description = $meta.description ModuleVersion = $meta.version #ProjectUri = "$meta.url" Guid = [guid]::newguid() PowerShellVersion = "2.0" #FormatsToProcess = "$fn.ps1xml" ProcessorArchitecture = 'amd64' FunctionsToExport = "*" AliasesToExport = "*" VariablesToExport = "" CmdletsToExport = "" PassThru = $true } New-ModuleManifest @psd | Out-File $target -Encoding UTF8 |
Added img/back.png.
cannot compute difference between binary files
Added img/clear.png.
cannot compute difference between binary files
Added img/clickycoloury.ico.
cannot compute difference between binary files
Added img/clipboard.png.
cannot compute difference between binary files
Added img/computer.png.
cannot compute difference between binary files
Added img/copy.png.
cannot compute difference between binary files
Added img/csv.png.
cannot compute difference between binary files
Added img/ethernet.png.
cannot compute difference between binary files
Added img/html.png.
cannot compute difference between binary files
Added img/icon.beta.png.
cannot compute difference between binary files
Added img/icon.cloud.png.
cannot compute difference between binary files
Added img/icon.cmd.png.
cannot compute difference between binary files
Added img/icon.controller.png.
cannot compute difference between binary files
Added img/icon.copy.png.
cannot compute difference between binary files
Added img/icon.date.png.
cannot compute difference between binary files
Added img/icon.events.png.
cannot compute difference between binary files
Added img/icon.exchange.png.
cannot compute difference between binary files
Added img/icon.extras.png.
cannot compute difference between binary files
Added img/icon.finduser.png.
cannot compute difference between binary files
Added img/icon.fire.png.
cannot compute difference between binary files
Added img/icon.firewall.png.
cannot compute difference between binary files
Added img/icon.folder.png.
cannot compute difference between binary files
Added img/icon.godzilla.png.
cannot compute difference between binary files
Added img/icon.info.png.
cannot compute difference between binary files
Added img/icon.install.png.
cannot compute difference between binary files
Added img/icon.key.png.
cannot compute difference between binary files
Added img/icon.license.png.
cannot compute difference between binary files
Added img/icon.lnk.png.
cannot compute difference between binary files
Added img/icon.log.png.
cannot compute difference between binary files
Added img/icon.mfcmapi.png.
cannot compute difference between binary files
Added img/icon.network.png.
cannot compute difference between binary files
Added img/icon.notes.png.
cannot compute difference between binary files
Added img/icon.office.png.
cannot compute difference between binary files
Added img/icon.powershell.png.
cannot compute difference between binary files
Added img/icon.printer.png.
cannot compute difference between binary files
Added img/icon.registry.png.
cannot compute difference between binary files
Added img/icon.samba.png.
cannot compute difference between binary files
Added img/icon.server.png.
cannot compute difference between binary files
Added img/icon.tools.png.
cannot compute difference between binary files
Added img/icon.unlock.png.
cannot compute difference between binary files
Added img/icon.users.png.
cannot compute difference between binary files
Added img/icon.wmi.png.
cannot compute difference between binary files
Added img/info.extras.png.
cannot compute difference between binary files
Added img/ping.png.
cannot compute difference between binary files
Added img/sweep.png.
cannot compute difference between binary files
Added img/to-user.png.
cannot compute difference between binary files
Added img/user.png.
cannot compute difference between binary files
Added modules/clipboard.psm1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | # api: ps # type: functions # title: clipboard # description: Reads/writes to clipboard # version: 0.8 # status: stable # category: win32 # config: - # # Provides clipboard read and write shortcuts, with an HTML formatter. # # syslib Add-Type -AN System.Windows.Forms #-- Send HTML formatted content to clipboard function Set-ClipboardHtml($html) { #-- escape non-ASCII characters first $html = [regex]::replace($html, "([^\x01-\x7F])", { "&#" + ([int][char][string]$args[0]) + ";" }) #-- wrap with HTML Format header $len = $html.length; # section sizes $pfx = 190; $start = 165; $end = 35; $data = @" Version:1.0 StartHTML:$(($pfx).toString().PadLeft(9, '0')) EndHTML:$(($pfx+$start+$len+$end).toString().PadLeft(9, '0')) StartFragment:$(($pfx+$start).toString().PadLeft(9, '0')) EndFragment:$(($pfx+$start+$len).toString().PadLeft(9, '0')) StartSelection:$(($pfx+$start).toString().PadLeft(9, '0')) EndSelection:$(($pfx+$start+$len).toString().PadLeft(9, '0')) SourceURL:http://localhost/#cmd-table <!DOCTYPE html><HTML> <HEAD><TITLE>Clibboard</TITLE><META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=UTF-8'></HEAD> <BODY><!--Start--> $html <!--End--></BODY> </HTML> "@ #-- send to system clipboard [System.Windows.Forms.Clipboard]::setData([System.Windows.Forms.Dataformats]::Html, $data) } #-- Write clipboard (text/plain) function Set-Clipboard($text) { if ($text.length) { [void] [System.Windows.Forms.Clipboard]::setText($text) } } #-- Return current content (text/plain) function Get-Clipboard() { return [System.Windows.Forms.Clipboard]::getText().trim() } <# #@tests return [System.Windows.Forms.Clipboard]::GetDataObject().getFormats() #> |
Added modules/menu.psm1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | # api: ps # type: functions # title: Utility code # description: Output and input shortcuts, screen setup, menu handling, meta extraction # version: 0.8.4 # category: misc # author: mario # config: # { name: cfg.hidden, type: bool, value: 0, description: also show hidden menu entries in CLI version } # status: beta # priority: core # # A couple of utility funcs: # · Get-Machine → Read-Host "machine" # · echo_n → echo wo/ CRLF # · Edit-Config → edit config file # · Extrac-PluginMeta → tokenize comment fields # · preg_match_all → PHP-esque regex matching # · Init-Screen → print script summary # · Print-Menu → output $menu # · Print-MenuHelp → show help= entries # · Process-Menu → input/run $menu prompt # # Load with: # Import-Module. ".\modules\menu.psm1" #-- get $machine function Get-Machine() { [CmdletBinding()] Param($current = $global:machine) # Ask for '$machine' Write-Host -N -f Yellow '$machine' # Add (default/last) if ($current) { Write-Host -N -f Gray "($current)" } Write-Host -N -f Yellow ': ' $new = (Read-Host).trim().toUpper() if (!$new) { return $current } # Ping test if (!(Test-Connection -ComputerName $new -Count 1 -BufferSize 128 -Quiet -ErrorAction SilentlyContinue)) { Write-Host -f Red " → not online" } $global:machine = $new return $new } #-- opens notepad for editing a list/csv file in-place function Get-NotepadCSV() { Param($text="", $EDITOR=$env:EDITOR) $tmpfn = [IO.Path]::GetTempFileName() $text | Out-File $tmpfn -Encoding UTF8 [void](Start-Process $EDITOR $tmpfn -Wait) $text = Get-Content $tmpfn | Out-String [void](Remove-Item $tmpfn) return $text } #-- echo sans newline function echo_n($str) { [void](Write-Host -NoNewLine $str) } #-- start notepad on config file (obsolete, now in separate script) function Edit-Config() { Param($fn, $options, $overwrite=0, $CRLF="`r`n", $EDITOR=$ENV:EDITOR) if ($overwrite -or !(Test-Path $fn)) { # create parent dir $dir = Split-Path $fn -parent if ($dir -and !(Test-Path $dir)) { md -Force "$dir" } # assemble defaults $out = "# type: config$CRLF# fn: $fn$CRLF$CRLF" $options | % { $v = $_.value switch ($_.type) { bool { $v = @('$False', '$True')[[Int32]$v] } default { $v = "'$v'" } } $out += '$' + $_.name + " = " + $v + "; # $($_.description)$CRLF" } # write $out | Out-File $fn -Encoding UTF8 } & $EDITOR "$fn" } #-- Regex/Select-String -Allmatches as nested array (convenience shortcut) function preg_match_all() { Param($rx, $str) $str | Select-String $rx -AllMatches | % { $_.Matches } | % { ,($_.Groups | % { $_.Value }) } } #-- baseline plugin meta data support function Extract-PluginMeta() { <# .SYNOPSIS Reads top-comment block and plugin meta data from given filename .DESCRIPTION Plugin meta data is a cross-language documentation scheme to manage application-level feature plugins. This function reads the leading comment and convers key:value entries into a hash. Also prepares the config{} parameter list. .PARAMERER fn Script to read from .OUTPUTS Returns a HashTable of field: values, including the config: list/hash. .EXAMPLE In ClickyColoury ir reads the "plugins" at once like this: $menu = (Get-Iten tools*/*.ps1 | % { Extract-PluginMeta $_ }) Entries than can be accessed like: $menu | % { $_.title -and $_.category -eq "network" } .NOTES Each entry contains an .id basename and .fn field, additionaly to what the plugin itself defines. Packs comment remainder as .doc field. #> Param($fn, $meta=@{}, $cfg=@()) # read file $str = Get-Content $fn | Out-String # look for first comment block if ($m = [regex]::match($str, '(?m)((?:^\s*[#]+.*$\n?)+)')) { # remove leading #␣ from lines, then split remainder comment $str = $m.groups[1] -replace "(?m)^\s*#[ \t]*", "" $str, $doc = [regex]::split($str, '\r?\n\r?\n') # find all `key:value` pairs preg_match_all -rx "(?m)^([\w-]+):\s*(.*(?:$)(?:\r?\n(?!\w+:).+$)*)" -str $str | % { $meta[$_[1]] = $_[2].trim() } # split out config: and crude-parse it (JSONish serialization) preg_match_all -rx "\{(.+?)\}" -str $meta.config | % { $r = @{}; preg_match_all -rx "([\w.-]+)\s*[:=]\s*(?:[']?([^,;}]+)[']?)" -str $_[1] | % { $r[$_[1]] = $_[2] }; $cfg += $r; } # merge into hashtable $meta.fn = "$fn" $meta.id = ($fn -replace "^.+[\\/]|\.\w+$","") -replace "[^\w]","_" $meta.doc = ($doc -join "`r`n") $meta.config = $cfg } return $meta # or return as (New-Object PSCustomObject -Prop $meta) } #-- script header function Init-Screen() { param($x=80,$y=45) #-- screen size better than `mode con` as it retains scrolling: if ($host.Name -match "Console") { $con = $host.UI.RawUI $buf = $con.BufferSize; $buf.height = 16*$y; $buf.width = $x; $con.BufferSize = $buf; $win = $con.WindowSize; $win.height = $y; $win.width = $x; $con.WindowSize = $win; } #-- header $meta = $cfg.main Write-Host -b DarkBlue -f White (" {0,-60} {1,15} " -f $meta.title, $meta.version) Write-Host -b DarkBlue -f Gray (" {0,-60} {1,15} " -f $meta.description, $meta.category) try { $host.UI.RawUI.WindowTitle = $meta.title } catch { } } #-- group plugin list by category, sort by sort: / key: / title: function Sort-Menu($menu) { $usort_cat = { (@{cmd=1; powershell=2; onbehalf=3; exchange=4; empirum=5; network=6; info=7; wmi=8}[$_.category], $_.category) -ne $null } $usort_key = { if ($_.key -match "(\d+)") { [int]$matches[1] } else { $_.key } } return ($menu | Sort-Object $usort_cat, {$_.sort}, $usort_key, {$_.title}) } #-- string cutting function substr($str, $from, $to) { if ($to -lt $str.length) { $str.substring($from, $to) } else { $str } } #-- Write out menu list (sorted, 3 columns, with category headers) function Print-Menu() { param($menu, $cat=".+", $last_cat="", $i=0) # group by category $ls = Sort-Menu ($menu | ? { $_.title -and $_.key -and ($_.category -match $cat) -and ((!$cfg.hidden) -or !$_.hidden) }) $ls | % { if ($last_cat -ne $_.category) { if ($line) { Write-Host ""} Write-Host -f Black (" {0,-74}" -f ($last_cat = $_.category)) $i = 0 } $line = (($i++) % 3 -ne 2) Write-Host -N -f Green ("{0,4}" -f (substr $_.key.split("|")[0] 0 4)) Write-Host -N -f DarkRed ("→") Write-Host -N:$line -f White ("{0,-21}" -f (substr $_.title 0 21)) } echo "" } #-- print help= entries from $menu (→ not very pretty) function Print-MenuHelp($menu) { $menu | ? { $_.title -and $_.key } | % { Write-Host -N -f Green (" " + $_.key.split("|")[0..2] -join ", ").PadRight(15) Write-Host -f White (" " + $_.title) Write-Host -f Gray (" " + ($_.description)) } } #-- Invoked on one menu entry → executes .command, or .func, or loads .fn script filter Process-MenuTask() { Param($params) $host.UI.RawUI.WindowTitle = "MultiTool → $($_.title)" Write-Host -b DarkBlue -f Cyan ("{0,-60} {1,18}" -f $_.title, $_.version) echo "" #-- commands or function try { if ($_.command) { Invoke-Expression $_.command # no options } elseif ($_.func) { Invoke-Expression "$($_.func) $($params)" # pass optional flags } #-- no fn? elseif (!$_.fn) { Write-Host -f Red "No processor for task:" $_ | FT | Write-Host } #-- start in separate "window" elseif ($_.type -eq "window") { Start-Process powershell.exe -ArgumentList "-STA -ExecutionPolicy ByPass -File $($_.fn) $global:machine" } #-- dot-source file simply (for e.g. "inline" and "cli" type) else { Invoke-Expression ". $($_.fn) $($params)" # run script } } catch { Write-Host -b DarkRed -f White ($_ | Out-String) $Error.Clear() } $host.UI.RawUI.WindowTitle = "MultiTool" } #-- Promp/input loop (REPL) function Process-Menu() { param($menu, $prompt="Func") #-- prompt+exec loop while ($True) { Write-Host -N -f Yellow "$prompt> " $which = (Read-Host).trim() if ($which -match "^([\w.-]+)(?:\b|\s|$)(.*)$") { $params = $matches[2] } else { $params = $null } # find according menu entry: run func or expression $menu | ? { $_.key -and $which -match "^($($_['key']))\b" } | % { while ($true) { $_ | Process-MenuTask $params if ((!$_.repeat) -or ((Read-Host "Wiederholen?") -notmatch "^[jy1rw]")) { break; } } } } } |
Added modules/starter.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | # api: ps # type: main # title: ClickyColoury + TextyTypey - GUI+CLI tool frontend # description: Convenience invocation of various Powershell and CMD scripts # version: 0.8.0 # depends: menu, wpf, clipboard # category: misc # config: # { name: cfg.gridview, type: select, value: Format-Table, select: Format-Table|Out-GridView, description: default table display mode } # { name: cfg.cli, type: bool, value: 0, description: Start console (CLI) version per default? } # { name: cfg.cached, type: bool, value: 0, description: Use CLIXML script cache on startup } # { name: debug, type: bool, value: 0, description: Powershell-internal } # author: mario # license: MITL # priority: core # status: testing # # Note that this is the WindowsPresentationForm version, but also renders a # classic -CLI menu otherwise. Utilizes: # # · wpf.psm1 = graphical toolkit features # · menu.psm1 = mostly CLI features # · clipboard.psm1 = for the HTML output # # Whereas scripts (and plugins) reside in tools*/*.ps1 # # Starting up with `-cli` parameter should yield the text version. # # Only works with powershell.exe -Version 2.0 -STA -File ... at the moment. # #-- params [CmdletBinding()] Param( [switch]$CLI = $false ) #-- config $global:cfg = @{ domain = "WORKWORKWORKWORKWORK" # (Get-ADDomain).Netbiosname threading = 1 autoclear = 300 exchange = @{ ConfigurationName = "Microsoft.Exchange" ConnectionUri = "http://YOUREXCHANGESRV/psremote/" } main = @{} curr_script_fn = $MyInvocation.MyCommand.Path multitool_base = (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)) user_config_fn = "$env:APPDATA\multitool\config.ps1" user_plugins_d = "$env:APPDATA\multitool" tools_cache_fn = "./data/tools.cache.clixml" tool_dirs = @("./tools/*/*.ps1") # add custom dirs here! } $global:plugins = @{ "init" = @() "before" = @() "after" = @() "menu" = @() } #-- load user config cd ($cfg.multitool_base) if (Test-Path ($cfg.user_config_fn)) { . ($cfg.user_config_fn) } $cfg|FL #-- restart if not in single-thread-apartment mode if (-not [System.Management.Automation.Runspaces.Runspace]::DefaultRunSpace.ApartmentState -eq "STA") { # powershell.exe -Version 2.0 -STA -ExecutionPolicy unrestricted -File $curr_script_fn # break 2 } #-- modules $global:GUI = [hashtable]::Synchronized(@{Host=$Host}) Import-Module -DisableNameChecking "$($cfg.multitool_base)\modules\wpf.psm1" Import-Module -DisableNameChecking "$($cfg.multitool_base)\modules\menu.psm1" Import-Module -DisableNameChecking "$($cfg.multitool_base)\modules\clipboard.psm1" if (!(Get-Module -Name ActiveDirectory)) { Import-Module ActiveDirectory } #-- post init $cfg.main = (Extract-PluginMeta $cfg.curr_script_fn) #$global:GUI|FL #-- predefined menu entries $menu = @( ) #-- load cache? if ($cfg.cached -and (Test-Path ($cfg.tools_cache_fn))) { $menu = Import-CliXml ($cfg.tools_cache_fn) } #-- add menu entries from scripts else { $menu += @(Get-Item ($cfg.tool_dirs) | % { Extract-PluginMeta $_ } | ? { $_.api -and $_.type -and $_.title }) } #-- add user plugins if (Test-Path ($cfg.user_plugins_d)) { $menu += @(Get-Item "$($cfg.user_plugins_d)/*.ps1" | % { Extract-PluginMeta $_ } | ? { $_.type -and $_.api -eq "multitool" }) } #-- run `type:init` plugins here $menu | ? { $_.type -eq "init" -and $_.fn } | % { . $_.fn } #-- CLI mode if ($CLI) { Init-Screen $menu = $menu + @{key="m|menu"; category="extras"; title="print menu"; func='Print-Menu $menu'} Print-Menu $menu Process-Menu -Menu $menu -Prompt "TextyTypey" } #-- WPF multi-tool else { echo "Starting GUI version..." $shell = Start-Win (Sort-Menu $menu) } #-- cleanup if ($Debug) { Remove-Module wpf $Error } |
Added modules/tasksched.psm1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # api: ps # type: init # category: function # title: Start-Sched # description: runs a GUI app via task scheduler (once) # version: 0.1 # src: https://msdn.microsoft.com/en-us/library/aa389389(v=vs.85).aspx # # Starts a GUI task via hidden task scheduler ("at"), # with a delay of 3 seconds to make up for network latency # and remote systime deviations. $command = "cmd.exe" # time = [datetime]::parse(GWMI -Class Win32_LocalTime -ComputerName $machine) $time = (Get-Date).AddSeconds(3) $args = @( $command, # cmd $time, # current +3s $False, # no repeat $Null, # days of week $Null, # days of month $True # interact with desktop ) # [wmiclass]"\\$machine\ROOT\CIMV2:Win32_ScheduledJob" Invoke-WmiMethod -class "Win32_ScheduledJob" -name "Create" -ArgumentList $args -ComputerName $machine |
Added modules/wpf.psm1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 | # api: ps # title: WPF + WinForms # description: WinForm and WPF shortcuts, GUI and thread handling # depends: menu, clipboard, sys:presentationframework, sys:system.windows.forms, sys:system.drawing # version: 1.0.7 # type: functions # category: ui # config: # { name: cfg.threading, type: bool, value: 1, description: Enable threading/runspaces for GUI/WPF version } # { name: cfg.autoclear, type: int, value: 300, description: Clear output box after N seconds. } # { name: cfg.noheader, type: bool, value: 0, description: Disable script info header in output. } # status: beta # priority: default # # Handles $menu list in WPF window. Creates widgets and menu entries # for plugin list / $menu entries. # · `type: inline` is the implied default, renders output in TextBlock # · `type: cli` or `window` plugins are run in a separate window # · `hidden: 1` tools only show up in menus # · `keycode:` is used for shortcuts; the CLI `key:` regex ignored # · `type: init-gui` plugins are run once during GUI construction # · Whereas `type: init` execute in the main/script RunSpace # # The responsive UI is achieved through: # · A new runspace for the GUI and a trivial message queue in $GUI.tasks. # · Main loop simply holds the window open, then executes $GUI.tasks events. # · That event queue simply holds the exact entries from $menu. # · Pipes them through `Run-GuiTask` (Should ideally be identical to the # one in menu.psm1, but needs a few customizations here.) # # All widget interactions are confined to the WPF runspace/thread. # · WPF interaction through `$GUI` would often hang both processes. # · Thus `Out-Gui` not only manages output, but also variable injection. # # Scripts/tools should work identically as for the CLI version: # · Aliases for `Write-Host` and `Read-Host` should make it transparent. # · However simple console output (typically to the stdout stream) will # have to be pipe-captured. # · Thus there's no guaranteed order for them and direct `Write-Host` calls. # # ToDo: # · split up into gui.psm1 and wpf.psm1 (actual GUI runspace) #-- register libs Add-Type -AN PresentationCore, PresentationFramework, WindowsBase Add-Type -AN System.Drawing, System.Windows.Forms, Microsoft.VisualBasic # [System.Windows.Forms.Application]::EnableVisualStyles() # [System.Windows.Forms.Application]::SetCompatibleTextRenderingDefault($true) #-- init vars $ModuleDir = Split-Path -Parent $MyInvocation.MyCommand.Definition $BaseDir = Split-Path -Parent $ModuleDir $global:GUI = [hashtable]::Synchronized(@{Host=$Host}) $menu = @() $global:last_output = 1.5 #-- WPF/WinForms widget wrapper function W { <# .SYNOPSIS Convenient wrapper to create WPF or WinForms widgets. .DESCRIPTION Allows to instantiate and modify WPF/XAML or WinForms UI widgets. The $prop hash unifies property assignments and widget method calls. Notably this implementation is a bit slow, due to this abstraction and probing different object trees. .PARAMERER type Widget type (e.g. "Button" or "Label") or existing $w widget object. .PARAMETER prop HashTable listing widget attributes or methods to call: @{Color="Red"; Text="Hello World"; Add=$other_widget} Property+method names depend on whether WPF or WinForms widgets are used. .PARAMETER type Defaults to "Controls" for WPF widgets, but can be set to "Forms" for WF. .OUTPUTS Returns the instantiated Widget. .EXAMPLE Create a button: $w_btn = W Button @{Content="Text"; Border=2; add_Click=$cb} Add child widgets per list: $w_grd = W Grid @{Add=$w1, $w2, $w3} Or chain creation of multiple widgets: $w_all = W StackPanel {Spacing=5; Add=( (W Button @{Content="OK"}), (W Label @{Content="Really?"}) )} The nesting gets confusing for WPF, but often simplifies WinForm structures. .NOTES The shortcut method `WF` creates WinForms widgets, whereas `WD` is for TextBlock/document inlines. #> [CmdletBinding()] Param($type = "Button", $prop = @{}, $Base="Controls") #-- new object if ($type.getType().Name -eq "String") { $w = New-Object System.Windows.$Base.$type } else { $w = $type } #@bug on FOOTERM01 w/ PS 3.0 if (($PSVersionTable.PSVersion.Major -eq 3) -and ($w -is [System.Windows.Thickness])) { return $w; } #-- apply options+methods $prop.keys | % { $key = $_ $val = $prop[$_] if ($pt = ($w | Get-Member -Force -Name $key)) { $pt = $pt.MemberType } #-- properties if ($pt -eq "Property") { if (($Base -eq "Forms") -and (@("Size" , "ItemSize", "Location") -contains $key)) { $val = New-Object System.Drawing.Size($val[0], $val[1]) } $w.$key = $val } #-- check for methods in widget and common subcontainers else { ForEach ($obj in @($w, $w.Children, $w.Child, $w.Container, $w.Controls)) { if ($obj.psobject -and $obj.psobject.methods.match($key) -and $obj.$key) { ([array]$val) | ? { $obj.$key.Invoke } | % { $obj.$key.Invoke($_) } | Out-Null break } } } } return $w } #-- WinForms version function WF { Param($type, $prop, $add=@{}, $click=$null) W -Base Forms -Type $type -Prop ($prop + @{add=$add; add_click=$click}) } #-- Document "widgets" function WD { Param($type, $prop=@{}) W -Base Documents -Type $type -Prop $prop } #-- WPF main window function WPF-Window { <# .SYNOPSIS Loads XAML file `wpf.xaml` from same directory as this module. .DESCRIPTION Also populates $GUI.$_ for a select few global widget names. .NOTES Workaround img\ cache dir not enabled. #> #-- ImgDir $ImgDir = $BaseDir #if (Test-Path "$($env:APPDATA)\multitool\img") { $ImgDir = "$($env:APPDATA)/multitool" } #-- load $xaml = ((Get-Content "$BaseDir/modules/wpf.xaml" | Out-String) -replace "e:/","$ImgDir/") $w = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader ([xml]$xaml))) #-- save aliases int $GUI (sync hash across all threads/runspaces) $GUI.w = $w $GUI.styles = $w.Resources $shortcuts = "Window,Menu,Ribbon,Grid_EXTRAS,Output,machine,username,bulkcsv" $shortcuts.split(",") | % { $GUI.$_ = $w.findName($_) } | Out-Null #--- return return $w } #-- create new runspace function New-GuiThread { <# .SYNOPSIS Creates a new Runspace for the GUI interface. .DESCRIPTION Copies functions over into new Runspace/Pipeline and attaches shared $GUI hashtable for data exchange. .EXAMPLE New-GuiThread {code} -Funcs "copy1,copy2" -Vars "menu,GUI" -Modules "WPF" .NOTES Does not work with PS 5.0 yet (presumably scoping or syntax issue with func duplication). #> Param( $code = {}, $funcs = "WPF-Window,W,WF,WD,Add-GuiMenu,Get-IconPath,Create-GuiParamFields,"+ "Out-Gui,Out-Html,Ask-Gui,Add-ButtonHooks,AD-Search,Get-MachineCurrentUser,"+ "Set-Clipboard,Set-ClipboardHtml,Get-Clipboard,Get-NotepadCSV,Extract-PluginMeta,preg_match_all", $modules = "", $vars = "menu", $level = "", [switch]$invoke=$true ) #-- create, add functions and code $shell = [PowerShell]::Create() $shell.AddScript(( ($funcs.split(",") | ? { $_ } | % { $func = Get-Command $_ -CommandType Filter,Function,Cmdlet "$($func.CommandType) $($func.Name) { $($func.Definition) };`r`n" }) -join "`r`n" )) | Out-Null #Write-Host -f green "'''$code'''" $shell.AddScript($code) | Out-Null #-- separate thread $shell.Runspace = [runspacefactory]::CreateRunspace() $shell.Runspace.ApartmentState = "STA" $shell.Runspace.ThreadOptions = "ReuseThread" $shell.Runspace.Open() | out-null #-- add vars, modules $shell.Runspace.SessionStateProxy.SetVariable("GUI", $GUI) | out-null $shell.Runspace.SessionStateProxy.SetVariable("vars", @{}) | out-null $shell.Runspace.SessionStateProxy.SetVariable("in_thread", 1) | out-null $vars.split(",") | ? { $_ } | % { $shell.Runspace.SessionStateProxy.SetVariable($_, (get-variable $_).value) } | Out-Null #$modules.split(",") | ?{$_} | % { $shell.Runspace.SessionStateProxy.ImportPSModule($_) } | Out-Null #-- start if ($invoke) { $handle = $shell.BeginInvoke() } return $shell } #-- no worky function Attach-StreamEventHandler { #-- attaches an event handler to runspace streams (.error and .warning) to capture output #$shell.AddScript({ # $GUI.stream_error = $Error #}) #Register-ObjectEvent -InputObject $shell.streams.error -EventName DataAdded -Action ({ # @{error="err"} | Out-String | Out-Gui -f Red #}) #$shell.streams.error.add_DataAdded({ # Param($sender, $event) # $Error | Out-Gui -f Red # @($Sender) | out-string | Out-Gui -f Red # [void]($sender.ReadAll() | % { $_.Message | Out-Gui }) # Out-Gui "error" -f Red #}) } #-- Look up icon basename path variations function Get-IconPath { <# .SYNOPSIS Looks up alternative filenames for icons. .DESCRIPTION9 Scans for PNGs (filename from PMD .icon or .img field) in $BaseDir/img/ .PARAMETER basenames Can be a list of icon names "user", "icon.user", "user2.png" etc. #> [CmdletBinding()] Param([Parameter(ValueFromRemainingArguments=$true)]$basenames) ForEach ($fn in ($basenames | ? { $_ -ne $null })) { ForEach ($png in "icon.$fn.png", "icon.$fn", "$fn.png", "$fn") { if (Test-Path ($path = "$BaseDir/img/$png")) { return $path } } } } #-- Additional input boxes / or combobox if according data/fieldname.txt file exists function Create-GuiParamFields() { <# .SYNOPSIS Creates input field widgets for extra #param: names .DESCRIPTION Some plugins may depend on more input than just `machine` and `username`. In order to avoid pesky VBS input field popups, plugins may specify additional fields with `# param: username,accesslevel,othervar3` This function crafts TextBox or ComboBox fields for those. Assigns them a unique (per-plugin) widget name "var_$plugin_$paramname". So it can later be read out by the "Read-Host" wrapper. .NOTES Combobox fields are created when there's an according data/combobox.$paramname.txt file. #> Param( $s = "extra,field,names", $prefix = "plugin", $extra_params = @() ) "$s".split("[;,]") | ? { $_ -and $_ -notmatch "^\s*(machine|host|computer|adname|account|user|bulk)" } | % { $key = $_.trim() if (Test-Path ($fn="data/combobox.$key.txt")) { $field = (W ComboBox @{Width=120; IsEditable="True"}) Get-Content $fn | % { $field.Items.Add((W ComboBoxItem @{Content=$_})) } | Out-Null $field.text = $field.Items[0].content # use first entry as default } else { $field = (W TextBox @{Width=120}) } $extra_params += (W WrapPanel @{Add=(W Label @{Content="$key"; FontWeight="Bold"}), $field}) $GUI.w.registerName("var_$($prefix)_$($key)", $field) } return $extra_params } #-- Add tool buttons to main window function Add-GuiMenu { <# .SYNOPSIS Adds menu entries and button blocks for each plugin from $menu .DESCRIPTION Iterators over the $menu list - skips "hidden:" or "nomenu:" entries, or "type:init*" plugins - adds a MenuItem and callback - uses the PMD .category to find the right menu or grid/notebook tab - looks up plugin or category icons (WPF requires unique instances each?!) - calls Create-GuiParamFields for extra input variables Also handles shortcut icons section. .NOTES This is what causes the slow startup! (perhaps due to `W` being too convenient) #> Param($menu) $icon_default = W Image ForEach ($e in $menu) { #-- prepare params $e.hidden = ($e.hidden -match "1|yes|hide|hidden|true") if ($e.type -eq "init") { continue; } $CAT = $e.category.toUpper(); $GRID = (@($GUI.w.findName("Grid_$CAT"), $GUI.Grid_EXTRAS) -ne $null)[0] #-- output category header if (($e.category -ne $category) -and (-not $e.hidden)) { $category = $e.category $GRID.Children.Add((W Label @{Content=" → $category"; Foreground="White"; Background="#443377"; Font="Verdana"; FontSize=17; Width=775})) } #-- callback (= now just appends to event queue) $cb = { $GUI.tasks += $e }.getnewclosure() #-- action block/button if (-not $e.hidden) { $border = W Border @{Style=$GUI.styles.ToolBlock; ToolTip=$e.doc; set_Child=( W StackPanel @{Orientation="Horizontal"; Add= (W Button @{Style=$GUI.styles.ToolButton; Width=120; Add_Click=$cb; Content=( W WrapPanel @{Add= (W Image @{Source=(Get-IconPath $e.img $e.icon $e.category); Height=20; Width=22}), (W TextBlock @{Text=$e.title; TextWrapping="Wrap"}) } )}), (W StackPanel @{Padding=2; Margin=4; Width=200; Add= @( (W TextBlock @{Text=$e.description; TextWrapping="Wrap"}), (W TextBlock @{Text="v$($e.version) - $($e.type)"; Foreground="#777777"}) ) + (Create-GuiParamFields $e.param $e.id) }) } )} $GRID.Children.Add($border) } #-- add menu entry if (($e.type -notmatch "^init") -and ($e.category)) { $m = $GUI.w.findName("Menu_$($CAT)") # new Extras > submenu if not found if (-not $m) { $m = (W MenuItem @{Name=$CAT; Header=$e.category}) $GUI.w.findName("Menu_EXTRAS").Items.Add($m) $GUI.w.registerName("Menu_$CAT", $m) } # add $ICON = W Image @{Source=(Get-IconPath $e.img $e.icon $e.category); Height=20; Width=20} $m.Items.Add((W MenuItem @{Header=$e.title; InputGestureText=$e.keycode; Icon=$ICON; ToolTip=(W ToolTip @{Content=$e.description}); Add_Click=$cb})) } } #-- and a shortcut - have their own custom sorting from `#shortcut: 123` if ($m = $GUI.w.findName("Shortcuts")) { ForEach ($e in ($menu | ? { $_.shortcut -match "\d+|true|yes" } | Sort-Object @{expression={$_.shortcut.trim()}} )) { $cb = { $GUI.tasks += $e }.getnewclosure() $ICON = W Image @{Source=(Get-IconPath $e.img $e.icon $e.category); Height=22; Width=22} $BTN = W Button @{Style=$GUI.styles.ActionButton; ToolTip=(W ToolTip @{Content=$e.title}); Add_click=$cb; Height=22; Width=22; Content=$ICON} $m.Children.Add($BTN) } } } #-- attach callbacks for main UI buttons function Add-ButtonHooks { <# .SYNOPSIS Prepares callbacks for buttons/toolbar in main window. .DESCRIPTION Such as the computer and username fields, or the clipboard functionality. .NOTES Ping and username lookup can freeze the UI, as they run in the WPF runspace already. $GUI.machine and $GUI.username are shared with the main thread. As is the clipboards $GUI.html shadow content. #> #-- computer name $GUI.w.findName("BtnComputer").add_Click({ if ($m = Get-Clipboard) { $GUI.machine.Text = $m $col = if (Test-Connection $m -Count 1 -Quiet -TTL 10 -ErrorAction SilentlyContinue) {"#5599ff99"} else {"#55ff7755"} $GUI.machine.Items.Insert(0, (W ComboBoxItem @{Content=$m; Background=$col})) $GUI.machine.Background = "$col" } }) $GUI.w.findName("BtnComputerClr").add_Click({ $GUI.machine.Text = ""; $GUI.machine.Background = "White" }) $GUI.w.findName("BtnComputerCpy").add_Click({ Set-Clipboard $GUI.machine.Text }) $GUI.w.findName("BtnComputerPng").add_Click({ }) $GUI.w.findName("BtnComputerUsr").add_Click({ if ($u = Get-MachineCurrentUser $GUI.machine.Text) { $GUI.username.Text = $u } }) #-- user name $GUI.w.findName("BtnUsername").add_Click({ $u = Get-Clipboard if ($u -match "\w+[@, ]+\w+") { $u = (AD-Search $u -only 1) } $GUI.username.Text = "$u" }) $GUI.username.add_DropDownOpened({ if (($u = $GUI.username.Text).length -gt 2) { $GUI.username.Items.Clear() AD-Search $u | % { $GUI.username.Items.Add($_) } } }) $GUI.username.add_DropDownClosed({ $GUI.username.Text = $GUI.username.Text -replace "\s*\|.+$", "" }) $GUI.w.findName("BtnUsernameClr").add_Click({ $GUI.username.Text = "" }) $GUI.w.findName("BtnUsernameCpy").add_Click({ Set-Clipboard $GUI.username.Text }) $GUI.w.findName("BtnUsernameCom").add_Click({ }) #-- bulk/csv $GUI.w.findName("BtnBulkimport").add_Click({ $GUI.bulkcsv.text = Get-NotepadCSV $GUI.bulkcsv.text "notepad" }) #-- clipboard tools $GUI.w.findName("BtnClipText").add_Click({ Set-Clipboard $GUI.output.text }) $GUI.w.findName("BtnClipHtml").add_Click({ # use $html when available if ($GUI.html) { Set-ClipboardHtml $GUI.html } else { #@ToDo: convert TextBlock.Inlines to HTML Set-Clipboard ($GUI.output.text) } }) $GUI.w.findName("BtnClipFree").add_Click({ $prev_output = ($GUI.output.Inlines | % { $_ }) $GUI.html = "" $GUI.output.text = "" }) $GUI.w.findName("BtnClipSwap").add_Click({ $GUI.output.text = "" $prev_output | % { $GUI.output.Inlines.Add($_) } }) $global:prev_output = @() #-- window closing $GUI.w.add_Closed({ $GUI.closed = $true }) #-- unicode symbols if ($ls = $GUI.w.findName("UnicodeClip")) { $cb = {Set-Clipboard $this.Content} ForEach ($btn in $ls.Children) { $btn.Add_click($cb) } } } #-- User lookup (simple `finduser` variant) function AD-Search { <# .SYNOPSIS Simple AD username search .DESCRIPTION Gets invoked whenever somethings is pasted into `username` input field. Defaults to search for "Last, First" names and spit out "UserName123". Also may scan for phone numbers or email addresses (used in UI for dropdown). .NOTES Defaults to [adsisearched], but may use Get-ADUser when available. By default the ActiveDirectory module is not loaded again into the WPF/UI Runspace. May require customization if AD displayname does not follow "First, Last" scheme. #> Param( $s = "", $only = 0 ) $s = ($s -replace "%","*") #-- AD search if (Test-Path function:Get-ADUser) { $filter = switch -regex ($s) { "^[*?\d]+$" { "telephoneNumber -like '*$s'" } "^\w+, \w+" { "displayname -like '$s*'" } "\w+@\w+" { "mail -like '$s'" } default { "displayname -like '$s*' -or samaccountname -like '$s*'" } } #-- find $u = Get-ADUser -Filter $filter -Properties samaccountname,displayname,telephonenumber } #-- ADSI query else { $s = $s -replace '([;\\"#+=<>])', '\$1' $filter = switch -regex ($s) { "^[*?\d]+$" { "telephonenumber=*$s" } "^\w+\\?, \w+" { "displayname=$s" } ".+\w+@\w+" { "mail=$s" } default { "|(displayname=$s*)(samaccountname=$s*)" } } $adsi = [adsisearcher]"(&(objectClass=user)(objectCategory=person)($filter))" [void]$adsi.PropertiesToLoad.AddRange(("telephonenumber","displayname","samaccountname","mail","*","+")) $u = ($adsi.findAll() | % { $_.properties }) } #-- result if ($only) { return [string]$u.samaccountname } else { return @($u | % { "{0} | {1} | {2}" -f [string]$_.samaccountname, [string]$_.displayname, [string]$_.telephonenumber }) } } #-- get current user function Get-MachineCurrentUser($m) { <# .SYNOPSIS Get current user from remote machine name. .DESCRIPTION Runs a quick WMI query. Returns user name (domain prefix stripped). .NOTES Does not fallback to alternative Explorer.exe process scan. #> if ($m -and ($w = gwmi win32_computersystem -computername $m) -and ($u = $w.username)) { return $u -replace "^\w+[\\\\]","" } } #-- HTML output # · invoked by Out-Gui function Out-Html { <# .SYNOPSIS Assembles HTML (clipboard) output from each Out-Gui (Write-Host alias) call. .DESCRIPTION Converts -F foreground and -B background colors and appends to $GUI.html. Additionally provides -Bold and -Underline support (but those aren't Write-Host/CLI compatible then). .NOTES That's the lazy approach. Original plan was to convert WPF TextBlock inlines in a HTML clipboard function. Postponed since Out-Gui does not implement any images or table output yet anyway. #> param( [Parameter(ValueFromPipeline=$true)]$str = $null, [alias("Foreground")]$f = $null, [alias("Background")]$b = $null, [alias("Bld")]$bold = $null, [alias("Strikethrough")]$S = $null, [alias("Underline")]$U = $null, [alias("NoNewLine")][switch]$N = $false, $css = "" ) #-- foreground/background maps (for white HTML background) $map_f = @{ Yellow = "#5554400" Red = "#55070F" Blue = "#111144" Green = "#004400" Cyan = "#004455" White = "#111" Gray = "#666" Black = "#222" } $map_b = @{ Yellow = "#f3f3aa" Green = "#aaeeaa" Blue = "#a5a5f5" Cyan = "#a1ece5" Red = "#ef8a77" White = "#444" Gray = "#bbb" Black = "#555" } #-- normalize string #if (!$str) { return } $str = ($str | Out-String -Width 120).trim() $str = $str -replace "(?m)\s*$", "" $str = $str -replace "<","<" $str = $str -replace ">",">" $str = $str -replace "\r?\n", "<br>`r`n" #-- colorize $str = $str -replace "✔","<font color=#115511>✔</font>" $str = $str -replace "✘","<font color=#771122>✘</font>" if ($f -eq "#ff9988dd") { $f = $null; # detect script title/description block $bold = "1"; $b = "#eeeef5"; } if ($f -and $map_f.containsKey($f)) { $f = $map_f[$f] # remap to darker foreground } if ($b) { if ($map_b.containsKey($b)) { $b = $map_b[$b] # lighter background colors } $css += "background-color:$b;" } if ($bold) { $str = "<strong>$str</strong>" } if ($U) { $str = "<u>$str</u>" } if ($css -or $f) { $str = "<font style='$css' color='$f'>$str</font>" } if ($N) { $NL = "" } else { $NL = "<br>`r`n" } $GUI.html += $str + $NL } #-- GUI interactions # · Primarily appends to `output` TextBlock # · Is called from main thread (and the only allowed interaction method), # itself uses w.Dispatcher.Invoke to execute codeblock in GUI runspace # · Also updates window `-Title` # · And provides `-GetVars` workaround filter Out-Gui { <# .SYNOPSIS Fills the $GUI.output TextBlock from calls to Write-Host (aliased to Out-Gui). .DESCRIPTION Mainly converts any objects (HashTables/PSObjects) to strings, then appends them to main output pane. Since this is called from the main script Runspace, uses the WPF dispatcher to execute the appending in the WPF runspace (factually WPF does it in another separate thread). .PARAM str Input string or object .PARAM f Foreground color .PARAM b Background color .PARAM N NoNewLine .PARAM bold, s Not CLI/Write-Host compatible. Should not be used unless the plugin/script was meant for GUI usage only. .NOTES Also implements another few UI interactions, such as -title setting, or output -clear. For example -getvars returns a HashTable of toolbar and plugin/ToolBlock input fields. Ultimately this should handle error/exception highlighting and creating OUtGrid-like display for HashTables and Objects. Not implemented yet, as it doesn't even run on PS 5.0 yet. Also should be rewritten to Begin{} Process{} End{} and split up to handle proper piping. (Currently this does not allow to synchronously handle output from tools/scripts. Write-Host calls get processed before any Out-Default remains...) #> param( [Parameter(ValueFromPipeline=$true)]$str = $null, [alias("Foreground")]$f = $null, [alias("Background")]$b = $null, [alias("Bld")]$bold = $null, [alias("Strikethrough")]$S = $null, [alias("Underline")]$U = $null, [alias("NoNewLine")][switch]$N = $false, [string]$title = $null, [string]$getvars = $null, [string]$testpfx = $null, [switch]$clear = $false ) trap { $_ | Out-String >> ./data/log.errors.wpf.txt } Out-Html $str -f $f -b $b -bold $bold -s $s -U $U -N:$N #-- wrap in scriptblock for GUI thread dispatcher $callback = [action]{ $inlines = @() trap { $_ | Out-String >> ./data/log.errors.out-gui.txt } #-- clear stale contents # · if last output over 5 minutes ago (`-clear:$true` is set by caller) if ($clear) { $GUI.prev_output = $GUI.output.inlines $GUI.output.text = "" $GUI.html = "" } #-- just -Title update if ($title) { $GUI.w.title = $title return } #-- Read input fields # · this is a workaround, because accessing $GUI.machine.text # directly from parent runspace would hang up WPF # · for some reason also needs hashtable recreated if ($getvars) { $GUI.vars = @{} $getvars.split("[,;]") | % { if (($key = $_.trim()) -and ($f = $GUI.w.findName($key)) -or ($f = $GUI.w.findName("var_$($testpfx)_$($key)"))) { $GUI.vars.$key = $f.text } } | Out-Null return $GUI.vars } # see if ErrorRecord try { $type = $str.GetType().Name } catch { $type = "unknown" } if ($type -eq "--ErrorRecord") { $inlines += WD Figure @{Background="#ff773322"; Content=($str|Out-String)} } # or HashTable elseif ($type -eq "---HashTable") { $inlines += ($str|Out-String) } # or image elseif ($type -eq "Image" -or $type -eq "Paragraph" -or $type -eq "InlineUIContainer") { # leave as-is } # plain string else { $str = $str | Out-String -Width 100 $a = WD Run @{Text=$str} if ($f) { $a.Foreground=$f } if ($b) { $a.Background=$b } if ($bold) { $a.FontWeight="Bold" } if ($S) { $a.TextDecorations="Strikethrough" } if ($U) { $a.TextDecorations="Underline" } $inlines += $a } #-- append $inlines | % { $GUI.output.Inlines.Add($_) } | Out-Null [void]$GUI.output.Parent.ScrollToBottom() } #-- PowerShell >= 3.0 does need the parameters swapped # (@src: https://gallery.technet.microsoft.com/Script-New-ProgressBar-329abbfd) if ($PSVersionTable.PSVersion.Major -eq 2) { [void]$GUI.w.Dispatcher.Invoke("Normal", $callback) } else { [void]$GUI.w.Dispatcher.Invoke($callback) } } ########################################################################################## ######################## everything below runs in main thread ######################## ########################################################################################## #-- User input # · this is aliased over `Read-Host` # → so scripts can be used unaltered in standalone/CLI or GUI mode # · for standard field names just returns the GUI $machine/$username input # · else shows a VB input box window function Ask-Gui { <# .SYNOPSIS Alias over `Read-Host` .DESCRIPTION Compares Read-Host requests against a list of know variable names/titles, such as "computer" or "username". If a match is found, returns the current value from the GUI input field. Else shows a VBS popup. The purpose is to allow tools/scripts to run in CLI mode as well as in GUI frontend unchanged. But to avoid needless popups for predefined input fields. .PARAM str Textual input query. Usually just the variable name '$computer' or '$user'. Aliases like 'Machine' and 'PC' and 'Hostname' are known, as well as 'AD-Account' or 'AD-User-Name' e.g. .NOTES Scripts should preferrably query for input in their Param() section once, and list custom fields per plugin meta #param: list. The GUI frontend does not implement a full PSHost, just an output TextBlock. (Input fields might be feasible, but too much work IMHO.) #> param($str, $title="Input", [switch]$N=$false) if ($str -match "^(?i)[$]?(AD[-\s]?)?(User|Account)(?:[-\s]?Name)?\s*[=:]*\s*?$") { return $GUI.vars.username } elseif ($str -match "^(?i)[$]?(Computer|Machine|PC|Host)([-\s]?Name)?\s*[=:]*\s*$") { return $GUI.vars.machine } elseif ($str -match "^(?i)[$]?(Bulk|bulk.?csv|bulkfn|list|CSV)") { return $GUI.vars.bulkcsv # should be exported to filename? } elseif ($GUI.vars.containsKey($str)) { #-- per-plugin input boxes return $GUI.vars[$str] } else { #-- Trivial input box for everything else return [Microsoft.VisualBasic.Interaction]::InputBox($title, $str, '') } } # alias for CMD `CHOICE` function function Ask-GuiVar { param($P, $VAR, $M, $TEXT) $v = Ask-Gui $TEXT "Choice" Invoke-Expression ('$global'+":$VAR = '$v'") } # alias for CMD `CHOICE` function function Ask-GuiMachine { return $global:machine } # alias for CMD `CHOICE` function function Ask-GuiUser { return $global:username } #-- Converts `# param: name,list` from vars{} to quoted cmdline "arg" "strings" function Get-ParamVarsCmd { <# .SYNOPSIS Crafts a list of cmd/Invoke-quoted strings from params list .DESCRIPTION Is used for type:window and type:cli scripts/plugins. Those get executed in a separate Powershell process, thus need input variables as exec arguments. .NOTES Somewhat tedious, as this is the third duplication of field/varname alias handling. #> Param( $param = "machine,username", # from plugin meta field $vars = @{machine=""; username=""}, # from GUI -GetVars $out = "" ) if (!$param) { $param = "machine,username" } # default list ForEach ($key in $param.split("[,;]")) { if (!($key = $key.trim()).length) { continue } # aliases if ($key -match '^(host|hostname|computer|pc-?name)$') { $key = "machine" } if ($key -match '^(user|ad-?name|account|accountname)$') { $key = "username" } if ($key -match '^(bulk|bulkcsv|bulkfn|bulklist)$') { $tmpfn = [IO.Path]::GetTempFileName() $vars[$key] | Out-File $tmpfn -Encoding UTF8 | Out-Null $vars[$key] = $tmpfn # ToDo: clean tmp via $plugins.after[] } # quote + append $out += ' "'+($vars[$key] -replace '([\\"^])','^$1')+'"' } return $out } #-- wrapper around Process-MenuTask function Run-GuiTask { <# .SYNOPSIS Executes the given $e event queue entry returned from GUI (menu or button callback). .DESCRIPTION Checks the $menu.type and handles output start, script execution, and result collection. - Just prints .title and .description first - Then assembles GUI variables ($machine, $username and extra script fields) - Runs `type:inline` plugins in main thread. - But `type:window` in separate window/CLI process. - Captures and outputs any errors - Then returns tp the Start-Win loop .PARAM e The script/tool entry as returned from $GUI.tasks (just one of the $menu entries really). Said queue gets filled by any of the menu or toolblock buttons in the main window. .NOTES Ideally this should just wrap the CLI Process-MenuTask function. The | Out-Gui piping does not mix direct Write-Host calls in order yet. Thus any extra script output gets shown AFTERWARDS. (to be fixed with proper pipe handling) #> [CmdletBinding()] param($e, $clear=$false) # one of the $menu entries{} #-- check last output time (>= 5 minutes) if ($last_output -and (([double]::parse((Get-Date -u %s)) - $last_output) -gt $cfg.autoclear)) { $clear = $true } #-- print header Out-Gui -title "➱ KlickiBunti → $($e.title)" -clear:$clear if ((!$e.noheader) -and (!$cfg.noheader)) { Out-Gui -f '#ff9988dd' -b '#ff102070' ("$($e.title) - $($e.description)") } #-- Get-GuiVars (fetch input from "Ribbon"-fields: machine, username, ...) Out-Gui -GetVars "machine,username,bulkcsv,$($e.param)" -testpfx $e.id # populates $GUI.vars{} $GUI.vars.GetEnumerator() | % { Set-Variable $_.name $_.value -Scope Global } #-- plugins TRAP { $_ | out-gui -b red } $plugins.before | % { Invoke-Expression ($_.ToString()) } #-- Run $menu entry rules (command=, func=, or fn=) try { #-- Internal commands if ($e.command) { [void]((Invoke-Expression $e.command) | Out-String | Out-Gui -f Yellow) } elseif ($e.func) { [void]((Invoke-Expression "$e.func $machine $username") | Out-String | Out-Gui -f Yellow) } #-- Start script elseif ($e.fn) { if ($e.type -match "window|cli") { # in separate window $cmd_params = Get-ParamVarsCmd ($e.param) ($GUI.vars) Start-Process powershell.exe -Argumentlist "-STA -ExecutionPolicy ByPass -File $($e.fn) $cmd_params" } else { # dot-source all "inline" type: plugins . $e.fn | Out-String | Out-Gui # -f Green } } #-- No handler else { Out-Gui -f Red "No Run-GuiTask handler for:" [void](@($e) | Out-String | Out-Gui) } } #-- Error cleanup catch { Out-Gui -f Yellow $_ } if ($Error) { $Error | % { $_ | Out-Gui -f Red } | Out-Null [void]($Error.Clear()) } #-- Run plugins / cleanup $plugins.after | % { Invoke-Expression ($_.ToString()) } $global:last_output = [double]::parse((Get-Date -u %s)) Out-Gui -title "➱ KlickiBunti" } #-- verify runspace and window are still active function Test-GuiRunning($shell, $GUI) { return ($shell.Runspace.RunspaceStateinfo.State -eq "Opened") -and (-not $GUI.closed) } #-- Initialize Window, Buttons, Subshell+GUI thread function Start-Win { <# .SYNOPSIS Starts the WPF window and Runspace, then waits for GUI events .DESCRIPTION Sets up GUI environment from $menu entries, then polls the shared $GUI. variables .NOTES Perhaps the least interesting function and self-explanatory. #> Param($menu) #-- no threading / manual # $GUI.w = $w = WPF-Window; $w.showdialog(); Write-Host "Ran WPF-Window without thread" #-- start GUI in separate thread/runspace $shell = New-GuiThread -Code { $GUI.w = $w = WPF-Window #TRAP { $_ | Out-Gui -f '#ffee99' -b '#cc5544'; $Error.Clear() } #TRAP { $GUI.w.Host.UI.Write($_); } #Import-Module ActiveDirectory Add-ButtonHooks Add-GuiMenu $menu #-- execute `type:init` files right away $menu | ? { $_.fn -and ($_.type -eq "init-gui") } | % { . $_.fn } $w.ShowDialog() } -Vars "menu,BaseDir,cfg,plugins" $GUI.tasks = @() #-- wait for window to be visible while (!($GUI.w.IsInitialized)) { Start-Sleep -milliseconds 275 Write-Host 'wait on $GUI.w.isInitialized' #$global:GUI |FT |Out-String|Write-Host -f Blue #$shell.runspace |FL -Prop *|Out-String|Write-Host -f Yellow #$shell.Streams |FL|Out-String|Write-Host -f DarkGreen if ($Debug -and $shell.streams.error) { $shell.streams | FL | Out-String | Write-Host -b Red } } #-- Alias console functions # `echo` is already alias to `Write-Output` Set-Alias -Name Write-Host -Value Out-Gui -Scope Global Set-Alias -Name Read-Host -Value Ask-Gui -Scope Global Set-Alias -Name Get-Machine -Value Ask-GuiMachine Set-Alias -Name choice -Value Ask-GuiVar #-- basic error catching TRAP { $_ | Out-Gui -f Red; $Error.Clear() } #-- main loop / trivial message queue # test for window state, pause a few seconds, then resolves $tasks[] while (Test-GuiRunning $shell $GUI) { if ($GUI.tasks) { $GUI.tasks | % { Run-GuiTask $_ } $GUI.tasks = @() } Start-Sleep -milliseconds 175 } } |
Added modules/wpf.xaml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | <!-- # api: wpf # type: gui # title: MultiTool window # version: 1.0.8 --> <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:r="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon" x:Name="Window" Title="➱ ClickyColoury" WindowStartupLocation="CenterScreen" Width="980" Height="720" ShowInTaskbar="True"> <!-- Styles --> <Window.Resources> <Style x:Key="RibbonGradient" TargetType="DockPanel"> <Setter Property="Background"><Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Color="#FFd1ddeb" Offset="0.00" /> <GradientStop Color="#ffc9d9ed" Offset="0.30" /> <GradientStop Color="#ffcdddef" Offset="0.70" /> <GradientStop Color="#ffd7e6f6" Offset="1.00" /> </LinearGradientBrush> </Setter.Value></Setter> </Style> <Style x:Key="RibbonBorder" TargetType="Border"> <Setter Property="BorderBrush" Value="#FF556699" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="CornerRadius" Value="8" /> <Setter Property="Margin" Value="3" /> <Setter Property="Padding" Value="4,3,4,0" /> <Setter Property="Background"><Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Color="#00d5e3f2" Offset="0.00" /> <GradientStop Color="#ffc9d9ed" Offset="0.20" /> <GradientStop Color="#ffcdddef" Offset="0.50" /> <GradientStop Color="#ffcdddef" Offset="0.51" /> <GradientStop Color="#ffd7e6f6" Offset="0.70" /> <GradientStop Color="#ffc2d9f1" Offset="0.71" /> <GradientStop Color="#ffa2c9e1" Offset="1.00" /> </LinearGradientBrush> </Setter.Value></Setter> </Style> <Style x:Key="RibbonBorderGreen" TargetType="Border" BasedOn="{StaticResource RibbonBorder}"> <Setter Property="Background"><Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Color="#00d5f2e3" Offset="0.00" /> <GradientStop Color="#ffc9edd9" Offset="0.20" /> <GradientStop Color="#ffb9e7d7" Offset="0.70" /> <GradientStop Color="#ffa0e4c5" Offset="0.71" /> <GradientStop Color="#ffa2e1c9" Offset="1.00" /> </LinearGradientBrush> </Setter.Value></Setter> </Style> <Style x:Key="ActionButton" TargetType="Button"> <Setter Property="TabIndex" Value="0" /> <Setter Property="FontSize" Value="18" /> <Setter Property="Foreground" Value="#ff333366" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border> <Border.Style> <Style TargetType="{x:Type Border}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#ffeeddaa"/> <Setter Property="Opacity" Value="1.0"/> </Trigger> <!--Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="#ffffcc99"/> <Setter Property="Opacity" Value="0.6"/></Trigger--> </Style.Triggers> </Style> </Border.Style> <Border Opacity="0.9"> <ContentPresenter/> </Border> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="ToolBlock" TargetType="Border"> <Setter Property="BorderBrush" Value="#ff707280" /> <Setter Property="BorderThickness" Value="2" /> <Setter Property="CornerRadius" Value="5" /> <Setter Property="Width" Value="350" /> <!--Setter Property="Height" Value="65" /--> <Setter Property="Margin" Value="5" /> <Setter Property="Padding" Value="4" /> <Setter Property="Background"><Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="#ffeeeeff" Offset="0.00" /> <GradientStop Color="#fff7f7f7" Offset="1.00" /> </LinearGradientBrush> </Setter.Value></Setter> </Style> <Style x:Key="ToolButton" TargetType="Button"> <Setter Property="FontSize" Value="15" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="BorderThickness" Value="3" /> </Style> <Style x:Key="MainTab" TargetType="TabItem"> <Setter Property="HeaderTemplate"> <Setter.Value> <DataTemplate> <ContentPresenter Content="{TemplateBinding Content}"> <ContentPresenter.LayoutTransform> <RotateTransform Angle="270" /> </ContentPresenter.LayoutTransform> </ContentPresenter> </DataTemplate> </Setter.Value> </Setter> <Setter Property="Padding" Value="0" /> <Setter Property="Margin" Value="0" /> <!--Setter Property="Width" Value="30" /--> </Style> </Window.Resources> <!-- Widgets --> <DockPanel Background="#77797c"> <Menu DockPanel.Dock="Top" Background="#ffc0d0ef"> <MenuItem Header="CMD" x:Name="Menu_CMD" ToolTip="Computer/command-line tools"> <MenuItem Header="OnBehalf" x:Name="Menu_ONBEHALF" ToolTip="OnBehalf remote execution" /> </MenuItem> <MenuItem Header="User" x:Name="Menu_USER" ToolTip="User accounts" /> <MenuItem Header="PowerShell" x:Name="Menu_POWERSHELL" ToolTip="Powershell scripts"> <MenuItem Header="Bulk" x:Name="Menu_BULK"><MenuItem.Icon><Image Source="e:/img/csv.png" Width="18" Height="18"/></MenuItem.Icon></MenuItem> </MenuItem> <MenuItem Header="Exchange" x:Name="Menu_EXCHANGE" ToolTip="Exchange/Outlook" /> <MenuItem Header="Network" x:Name="Menu_NETWORK" ToolTip="Network and server"> <MenuItem Header="Server" x:Name="Menu_SERVER"><MenuItem.Icon><Image Source="e:/img/icon.server.png"/></MenuItem.Icon></MenuItem> </MenuItem> <MenuItem Header="Info" x:Name="Menu_INFO" ToolTip="Info (read-only) tools for users/machines" /> <MenuItem Header="WMI" x:Name="Menu_WMI" ToolTip="Windows Management Interface" /> <MenuItem Header="UserTools" x:Name="Menu_USERTOOLS" ToolTip="Shortcuts installed on \\$machine\c:\Users\$username\Desktop" /> <MenuItem Header="Beta" x:Name="Menu_BETA" ToolTip="New/experimental scripts" /> <MenuItem Header="_Extras" x:Name="Menu_EXTRAS" ToolTip="Config, Miscellaneous, Shortcuts"> <MenuItem Header="Misc" x:Name="Menu_MISC"><MenuItem.Icon><Image Source="e:/img/icon.controller.png"/></MenuItem.Icon></MenuItem> <MenuItem Header="Config" x:Name="Menu_CONFIG"><MenuItem.Icon><Image Source="e:/img/icon.tools.png"/></MenuItem.Icon></MenuItem> <MenuItem Header="Update" x:Name="Menu_UPDATE"><MenuItem.Icon><Image Source="e:/img/icon.log.png"/></MenuItem.Icon></MenuItem> <!--MenuItem Header="TEST scripts" x:Name="Menu_TEST"><MenuItem.Icon><Image Source="e:/img/icon.godzilla.png"/></MenuItem.Icon></MenuItem--> </MenuItem> <MenuItem Header="✐" x:Name="Menu_EDIT" ToolTip="Script editing" /> <MenuItem Header="䷰" HorizontalAlignment="Right" x:Name="Menu_DOCS" ToolTip="w/ blackjack" /> </Menu> <DockPanel x:Name="Ribbon" DockPanel.Dock="Top" Height="80" Style="{StaticResource RibbonGradient}"> <!-- Computer --> <Border Style="{StaticResource RibbonBorder}" Width="170" DockPanel.Dock="Left"> <DockPanel> <!-- Btn --> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom" FontWeight="Bold" Foreground="#ff223366" Content="Machine/Hostname"/> <Button DockPanel.Dock="Left" x:Name="BtnComputer" Width="64" Height="64" Style="{StaticResource ActionButton}" ToolTip="Computer (←clipbrd)"><Image Source="e:/img/computer.png" Width="64" Height="64" /></Button> <DockPanel> <!-- Clear/Copy/Ping/ToUser --> <WrapPanel DockPanel.Dock="Top"> <Button x:Name="BtnComputerClr" Style="{StaticResource ActionButton}" ToolTip="Clear" Margin="0,0,5,0"><Image Source="e:/img/clear.png" Width="16" Height="16" Opacity="0.2" /></Button> <Button x:Name="BtnComputerCpy" Style="{StaticResource ActionButton}" ToolTip="Copy" Margin="0,0,10,0"><Image Source="e:/img/copy.png" Width="16" Height="16" Opacity="0.5" /></Button> <Button x:Name="BtnComputerPng" Style="{StaticResource ActionButton}" ToolTip="ping" Margin="0,0,5,0"><Image Source="e:/img/ping.png" Width="16" Height="16" Opacity="0.4" /></Button> <Button x:Name="BtnComputerUsr" Style="{StaticResource ActionButton}" ToolTip="Get current user →"><Image Source="e:/img/to-user.png" Width="18" Height="16" Opacity="0.8" /></Button> </WrapPanel> <!-- Input --> <ComboBox x:Name="machine" IsEditable="True" Height="22" Width="90" FontSize="13" FontWeight="Bold" /> </DockPanel> </DockPanel> </Border> <!-- User --> <Border Style="{StaticResource RibbonBorder}" Width="175" DockPanel.Dock="Left"> <DockPanel> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" FontWeight="Bold" Foreground="#ff223366" Content="User target"/> <Button DockPanel.Dock="Left" x:Name="BtnUsername" Style="{StaticResource ActionButton}" ToolTip="User (←clipbrd)"><Image Source="e:/img/user.png" /></Button> <DockPanel> <!-- Clear/Copy/ToComputer --> <WrapPanel DockPanel.Dock="Top"> <Button x:Name="BtnUsernameClr" Style="{StaticResource ActionButton}" ToolTip="Clear" Margin="0,0,5,0"><Image Source="e:/img/clear.png" Width="16" Height="16" Opacity="0.2" /></Button> <Button x:Name="BtnUsernameCpy" Style="{StaticResource ActionButton}" ToolTip="Copy" Margin="0,0,50,0"><Image Source="e:/img/copy.png" Width="16" Height="16" Opacity="0.5" /></Button> <Button x:Name="BtnUsernameCom" Style="{StaticResource ActionButton}" ToolTip="← To primary computer"><Image Source="e:/img/to-user.png" Width="18" Height="16" Opacity="0.2" /></Button> </WrapPanel> <!-- Input --> <ComboBox x:Name="username" IsEditable="True" Height="22" Width="120" FontSize="13" FontWeight="Bold" /> </DockPanel> </DockPanel> </Border> <!-- Bulk --> <Border Style="{StaticResource RibbonBorder}" Width="135" DockPanel.Dock="Left"> <DockPanel> <DockPanel DockPanel.Dock="Left"> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" FontWeight="Bold" Foreground="#ff223366" Content="Bulk"/> <Button DockPanel.Dock="Top" x:Name="BtnBulkimport" Style="{StaticResource ActionButton}"><Image Source="e:/img/csv.png" /></Button> </DockPanel> <TextBox DockPanel.Dock="Right" x:Name="bulkcsv" AcceptsReturn="True" Height="64" Width="120" FontSize="10" /> </DockPanel> </Border> <!-- Shortcuts --> <Border Style="{StaticResource RibbonBorder}" Width="85" DockPanel.Dock="Left"> <DockPanel> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" FontWeight="Bold" Foreground="#ff223366" Content="Shortcuts"/> <WrapPanel DockPanel.Dock="Top" x:Name="Shortcuts" Width="82" Height="80" /> </DockPanel> </Border> <!-- Unicode --> <Border Style="{StaticResource RibbonBorderGreen}" Width="155" DockPanel.Dock="Left"> <DockPanel> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" FontWeight="Bold" Foreground="#ff223366" Content="Unicode"/> <ScrollViewer DockPanel.Dock="Top"> <WrapPanel x:Name="UnicodeClip" Width="140" Height="66"> <Button Style="{StaticResource ActionButton}" Content="❏" /> <Button Style="{StaticResource ActionButton}" Content="✔" /> <Button Style="{StaticResource ActionButton}" Content="✘" /> <Button Style="{StaticResource ActionButton}" Content="➜" /> <Button Style="{StaticResource ActionButton}" Content="➩" /> <Button Style="{StaticResource ActionButton}" Content="▶" /> <Button Style="{StaticResource ActionButton}" Content="⏩" /> <Button Style="{StaticResource ActionButton}" Content="✎" /> <Button Style="{StaticResource ActionButton}" Content="❍" /> <Button Style="{StaticResource ActionButton}" Content="☑" /> <Button Style="{StaticResource ActionButton}" Content="☒" /> <Button Style="{StaticResource ActionButton}" Content="⟳" /> <Button Style="{StaticResource ActionButton}" Content="❎" /> <Button Style="{StaticResource ActionButton}" Content="☛" /> <Button Style="{StaticResource ActionButton}" Content="♻" /> <Button Style="{StaticResource ActionButton}" Content="⚙" /> <Button Style="{StaticResource ActionButton}" Content="✰" /> <Button Style="{StaticResource ActionButton}" Content="✱" /> <Button Style="{StaticResource ActionButton}" Content="☎" /> <Button Style="{StaticResource ActionButton}" Content="➟" /> <Button Style="{StaticResource ActionButton}" Content="⚡" /> <Button Style="{StaticResource ActionButton}" Content="⤷" /> <Button Style="{StaticResource ActionButton}" Content="❚" /> <Button Style="{StaticResource ActionButton}" Content="〓" /> <Button Style="{StaticResource ActionButton}" Content="❯" /> </WrapPanel> </ScrollViewer> </DockPanel> </Border> <!-- Clipboard --> <Border Style="{StaticResource RibbonBorderGreen}" Width="125" DockPanel.Dock="Right"> <DockPanel> <Label DockPanel.Dock="Bottom" HorizontalAlignment="Center" FontWeight="Bold" Foreground="#ff223366" Content="Clipboard"/> <Button DockPanel.Dock="Left" x:Name="BtnClipText" ToolTip="Copy (text)" Width="48" Height="48" Style="{StaticResource ActionButton}"><Image Source="e:/img/clipboard.png" /></Button> <Button DockPanel.Dock="Left" x:Name="BtnClipHtml" ToolTip="Copy (colored)" Width="48" Height="48" Style="{StaticResource ActionButton}"><Image Source="e:/img/html.png" /></Button> <Button DockPanel.Dock="Top" x:Name="BtnClipFree" ToolTip="Clear" Width="24" Height="20" Style="{StaticResource ActionButton}"><Image Source="e:/img/sweep.png" /></Button> <Button DockPanel.Dock="Bottom" x:Name="BtnClipSwap" ToolTip="Last buffer" Width="24" Height="20" Style="{StaticResource ActionButton}"><Image Source="e:/img/back.png" /></Button> </DockPanel> </Border> </DockPanel> <!-- Main --> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="2" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TabControl TabStripPlacement="Left" Background="White"> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.cmd.png" Width="20" Height="20" /> <Label Content="CMD" FontSize="14" FontWeight="Bold" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_CMD" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.user.png" Width="20" Height="20" /> <Label Content="Empirum" FontSize="14" FontWeight="Bold" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_USER" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.powershell.png" Width="20" Height="20" /> <Label Content="Power" FontSize="14" FontWeight="Bold" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_POWERSHELL" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.exchange.png" Width="20" Height="20" /> <Label Content="Exchange" FontSize="14" FontWeight="Bold" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_EXCHANGE" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.info.png" Width="20" Height="20" /> <Label Content="Info" FontSize="14" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_INFO" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.beta.png" Width="20" Height="20" /> <Label Content="Beta" FontSize="14" FontWeight="Bold" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_BETA" Background="#ff777a87" /> </ScrollViewer> </TabItem> <TabItem Style="{StaticResource MainTab}"> <TabItem.Header> <StackPanel Orientation="Horizontal"> <Image Source="e:/img/icon.tools.png" Width="20" Height="20" /> <Label Content="Extras" FontSize="14" /> </StackPanel> </TabItem.Header> <ScrollViewer> <WrapPanel x:Name="Grid_EXTRAS" Background="#ff777a87" /> </ScrollViewer> </TabItem> </TabControl> <Grid Grid.Column="2" Width="1" HorizontalAlignment="Stretch" Background="#ff444444" /> <GridSplitter Grid.Column="3" Width="7" HorizontalAlignment="Stretch" Background="#ff444444" /> <ScrollViewer Grid.Column="4"> <TextBlock x:Name="Output" Padding="2" FontSize="12" FontFamily="Consolas,Mono" FontWeight="Normal" TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Foreground="White" Background="#012356"></TextBlock> </ScrollViewer> </Grid> </DockPanel> </Window> |
Added tools/beta/copy_driver.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | # api: multitool # version: 0.3.7 # title: Copy driver # description: from X:\Treiber\ to user \Temp # type: window # category: powershell # img: copy # hidden: 0 # status: untested # param: machine, driver # # ServiceDesk Driver Collection # ❏ from X:\drivers # ❏ coypied to user \\$machine\C$\Temp\ # #-- vars Param( $machine = (Read-Host "Computer"), $driver = (Read-Host "Driver"), $cache_fn = "data/combobox.driver.txt", $driver_d = "X:\Drivers", $CRLF = "`r`n" ) #-- update list if ($driver -match "^-*update-*(list)?$") { Write-Host -f Green "❏ updating $cache_fn" $r = "update-list" ForEach ($fn in GCI $driver_d) { $r += "$CRLF$fn" } $r | Out-File $cache_fn -Encoding UTF8 } #-- else copy elseif (Test-Connection -Quiet $machine) { md "\\$machine\c$\Temp\$driver" robocopy /E /V /B "$driver_d\$driver" "\\$machine\c$\Temp\$driver" Write-Host -f Green "-- Close me window --" Start-Sleep -seconds 20 } |
Added tools/cmd/cmd_boottime.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # api: multitool # version: 2.0 # title: Boot time # description: Ermittle System-Boot-Zeit # type: psexec # category: cmd # img: date.png # hidden: 0 # key: 3|boot # src: https://github.com/lazywinadmin/LazyWinAdmin_GUI/blob/master/LazyWinAdmin/LazyWinAdmin.ps1#L159 # # Now queries via WMI Win32_OperatingSystem ➔ Lastbootuptime # # CMD approach: # ❏ systeminfo /s $machine # Param($machine = (Read-Host "Computer")) #-- new WMI $wmi = Get-WmiObject -class Win32_OperatingSystem -computer $machine $uptime = $wmi.ConvertToDateTime($wmi.Lastbootuptime) Write-Host "Last boot time: $uptime" |
Added tools/cmd/cmd_currentuser.ps1.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # api: multitool # version: 1.0 # title: Current user on PC # description: Get currently logged on user (via PSLoggedOn.exe) # type: psexec # category: cmd # img: users.png # hidden: 0 # key: 6|cu|current|currentuser|loggedon|psloggedon # config: - # # Via PsLoggedOn.exe # · There are quicker alternatives for Powershell now... # · e.g. WMI Win32_user Param($machine = (Read-Host "Computer")) Write-Host "PSLoggedOn..." U:\Tools\PsLoggedon.exe \\$machine |
Added tools/cmd/cmd_sfc.ps1.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # api: multitool # version: 1.0 # title: SFC /SCANNOW # description: scan protected system files (and replace) # type: window # category: cmd # hidden: 0 # key: 5|sfc # config: - # # runs sfc /scannow on remote computer Param($machine = (Read-Host "Computer")) Write-Host "Starting SFC /SCANNOW..." psexec \\$machine sfc /scannow Read-Host "---END---" |
Added tools/exchange/serverchecks.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # api: multitool # version: 0.5 # title: Exchange health checks # description: probe various server performance/database stats # type: inline # category: exchange # icon: firewall # param: exchangeserver # hidden: 0 # key: m10|exchangetests? # status: beta # config: - # # Run basic Exchange server health checks # ❏ Test-ServiceHealth # ❏ Test-Mailflow # ❏ Get-MailboxDatabase # ❏ Get-MailboxDatabase Param($server = (Read-Host "exchangeserver")); #-- conn Import-ExchangeSession #-- tests Write-Host -f Green "❏ Test-ServiceHealth" Test-ServiceHealth | FL | Out-String | Write-Host Write-Host -f Green "❏ Test-EcpConnectivity" Test-EcpConnectivity -ClientAccessServer $server | Out-String | Write-Host Write-Host -f Green "❏ Test-Mailflow" Test-Mailflow -Targetmailboxserver $server | FL -Prop * | Out-String | Write-Host Write-Host -f Green "❏ Get-MailboxDatabase -Status" Get-MailboxDatabase -Status -Server $server | FT name,server,mounted,replicationtype,recovery -Auto -Wrap | Out-String | Write-Host Write-Host -f Green "❏ Get-Queue" Get-Queue -Server $server | FL -Prop PSComputerName,Identity,IsValid,Status,MessageCount,RetryCount,LastError,RiskLevel,IncomingRate,OutgoingRate,PriorityDescriptions,DeferredMessageCount,LockedMessageCount | Out-String | Write-Host |
Added tools/extras/eventvwr.ps1.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # api: multitool # version: 0.1 # title: EventVwr # description: start EventViewer on remote computer # type: inline # category: extras # img: tools.png # hidden: 1 # noheader: 1 # key: t9|eventvwr # config: - # # End Start-Process eventvwr.exe -ArgumentList $machine |
Added tools/extras/exit.ps1.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # api: multitool # version: 0.0 # title: Exit MultiTool # description: end WPF multitool # type: inline # category: extras # hidden: 1 # icon: fire.png # key: x0|x|quit|exit # keycode: Alt+F4 # config: - # # # End break 5 |
Added tools/extras/powershell_info.ps1.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # api: multitool # version: 0.0 # title: PowerShell info # description: runtime details # type: inline # category: misc # hidden: 1 # icon: powershell # config: - # Write-Host "❏ Host" ($Host |FL | out-string).trim() |write-host Write-Host "❏ VerTbl" ($PSVersionTable |Ft | out-string).trim() |write-host Write-Host "❏ [Accels]" ([psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get | fT -auto -wrap | out-string -width 60).trim() | write-host |
Added tools/info/DCs.ps1.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # api: multitool # version: 1.3 # title: Domain controllers # description: List DCs # type: inline # category: info # hidden: 0 # key: i1|DCs|domain|controllers|pdc # config: {} # # Shows list of active domain controlles Get-ADDomainController -Filter * | FT -Auto -Wrap Name,Enabled,Site,IPV4Address,SslPort,LdapPort,IsReadOnly | Out-String -Width 100 |
Added tools/info/dhcp.ps1.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # api: multitool # version: 1.0 # title: DHCP servers # description: List domain DHCP servers # type: cmd # category: info # hidden: 0 # key: i2|dhcp|lsdh # config: {} # # List domain DHCP servers netsh dhcp show server |
Added tools/info/dns.ps1.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # api: multitool # version: 0.1 # title: DNS servers # description: List domain DNS via dsquery # type: cmd # category: info # hidden: 0 # key: i3|dns|dsquery # config: {} # # Runs `dsquery` to show all DNS servers for domain. dsquery server -limit 0 |
Added tools/info/findlocaladmins.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # api: multitool # version: 0.5 # title: Detect admins # description: list local admins on remote machine # type: inline # category: info # img: users.png # hidden: 0 # key: i13|localadmins # config: {} # # list local admins on computer # - via WMI win32_groupuser # - shortened to DOMAIN\UserName Param($machine = (Read-Host "Machine")) Write-Host -f Green "WMI query 'win32_groupuser'..." $admins = Gwmi win32_groupuser –computer $machine Write-Host -f Green "Extract account infos" $admins = $admins |? {$_.groupcomponent –like '*"Administrators"'} $admins | ? { $_.PartComponent -match 'Domain="(.*)",Name="(.*)"' } | % { Write-Host -f Yellow "♞ $($matches[1])\$($matches[2])" } |
Added tools/info/fu.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # api: multitool # version: 1.4 # title: Find user # description: Performs an AD search for phone numbers or employee numbers # type: inline # category: info # hidden: 0 # icon: finduser # key: i4|fu|f|find|find-?user # keycode: F6 # shortcut: 4 # config: - # # Scans AD user list for all phone numbers, or for employee IDs. # # → The graphical WPF-MultiTool has a simpler version built into # the `User` field/dropdown already (- only scans for the main # telephone number though). # # → Should use Out-GridView or Format-Table per config option. # (Not reimplemented yet for GUI or CLI version). Param($find = (Read-Host "User")); # adapt for LIKE if ($find -match "%") { # SQL placeholders $find = $find -replace "%","*" } if ($find -notmatch "\*") { if ($find -match "[A-Z]{3,}") { # usernames $find = "*$find*" } else { # numbers only $find = "*$find" } } # search $ls = (Get-ADUser -Filter { (TelephoneNumber -like $find) -or (MobilePhone -like $find) -or (employeeNumber -like $find) -or (homePhone -like $find) -or (displayname -like $find) } -Properties samaccountname,displayname,telephoneNumber,mobilePhone,homePhone,employeeNumber | Select-Object samaccountname,displayname,telephonenumber,mobilephone,homePhone,employeeNumber) # output if ($cfg.gridview -match "GridView") { $ls | Out-GridView } else { $ls | Format-Table -Auto -Wrap | Out-String -Width 120 } |
Added tools/info/get_rdp_perm.ps1.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # api: multitool # version: 0.3 # title: get RDP permissions # description: on remote PC # type: inline # category: info # img: registry # hidden: 0 # # SYSTEM\CurrentControlSet\\Control\\Terminal Server\fDenyTSConnections $v = Get-RemoteRegistry("\\$machine\HKLM\SYSTEM\CurrentControlSet\\Control\\Terminal Server\fDenyTSConnections") Write-Host "RDP = $(!$v)" |
Added tools/info/lockedusers.ps1.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # api: multitool # version: 1.0 # title: Locked out users # description: Find locked out user names # type: inline # category: info # icon: user # hidden: 0 # key: i5|lock|locked|lockedout|ll # config: {} # # Scans AD for locked-out accounts Search-ADAccount -LockedOut | Select samaccountname, name |
Added tools/info/minicmdlets.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # api: multitool # version: 0.1 # title: mini tools # description: Small PS cmdlet collection # type: inline # category: info # hidden: 0 # param: machine,username,minicmdlets # config: {} # # Various functions Param( $machine = (Read-Host "Machine"), $user = (Read-Host "User"), $cmd = (Read-Host "minicmdlets") ) #-- run Write-Host -f Green "❏ $cmd" $cmd = $cmd -replace '\$(machine|host|computer)',"'$machine'" $cmd = $cmd -replace '\$(username|user|account)',"'$user'" (Invoke-Expression $cmd | Out-String).trim() | Write-Host |
Added tools/info/netuser.ps1.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # api: multitool # version: 0.1 # title: NET USER # description: Get NET USER details # type: inline # category: info # hidden: 0 # key: i6|netuser # config: {} # # NET USER Param($username = (Read-Host "USer")) NET USER $username /DOMAIN |
Added tools/info/printserver.ps1.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # api: multitool # version: 1.1 # title: Printservers # description: scans AD for print servers # type: inline # category: info # hidden: 0 # key: i7|print|print-?se?rve?r? # config: {} # # scans AD for print servers $ls = Get-ADObject -LDAPFilter "(&(&(&(uncName=*)(objectCategory=printQueue))))" -Prop * | Sort-Object -Unique -Property servername | Select servername $ls | Format-Table -Auto -Wrap |
Added tools/info/service.ps1.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # api: multitool # version: 0.2 # title: Services # description: list services on remote machine # type: inline # category: info # hidden: 0 # key: i8|service|services # config: {} # # list services on remote machine Param($machine = (Read-Host "Machine")) Get-Service -computer $machine | Select-Object status,name,description | FT |
Added tools/network/nslookup.ps1.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | # api: multitool # version: 0.1 # title: NSlookup # description: nameserver lookup # type: inline # category: network # key: n2|tracert # config: - # # nslookup nslookup $machine |
Added tools/network/ping.ps1.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | # api: multitool # version: 0.1 # title: Ping # description: Ping computer # type: inline # category: network # key: n1|ping # config: - # # Ping ping $machine -n 3 |
Added tools/network/tracert.ps1.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | # api: multitool # version: 0.1 # title: Tracert # description: Traceoute to computer # type: inline # category: network # key: n3|tracert # config: - # # Ping tracert $machine |
Added tools/plugins/configedit.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # api: multitool # version: 0.4 # title: Config file # description: Create and edit config file # type: inline # category: config # hidden: 1 # key: config|cfg # img: tools # config: - # # # Create/load main config file #-- init Param($fn="$env:APPDATA\multitool\config.ps1", $options=@(), $overwrite=0, $CRLF="`r`n", $EDITOR="notepad") # create parent dir if ($overwrite -or !(Test-Path $fn)) { $dir = Split-Path $fn -parent if ($dir -and !(Test-Path $dir)) { md -Force "$dir" } } #-- read file if (Test-Path $fn) { $src = (Get-Content $fn) | Out-String } else { $src = "# type: config$CRLF# fn: $fn$CRLF$CRLF" } #-- fetch options from all plugins $options = @($menu | ? { $_.config -and $_.config.count } | % { $_.config }) #-- and main $options += (Extract-PluginMeta "./modules/starter.ps1").config $options += (Extract-PluginMeta "./modules/menu.psm1").config $options += (Extract-PluginMeta "./modules/wpf.psm1").config #-- assemble defaults $options | % { $v = $_.value switch ($_.type) { bool { $v = @('$False', '$True')[[Int32]$v] } default { $v = "'$v'" } } if ($src -notmatch "(?mi)^[$]$($_.name)") { $src += '$' + $_.name + " = " + $v + "; # $($_.description)$CRLF" } } # (over)write $src | Out-File $fn -Encoding UTF8 #-- start notepad & $EDITOR "$fn" |
Added tools/plugins/funcs_base.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | # encoding: utf-8 # api: ps # title: functions # description: Some utility functions for scripts # version: 1.0 # type: init # category: misc # hidden: 1 # priority: optional # # Defines: # · PSExec → wrap psexec.exe # · Check-PSExecResult → eval $LASTEXITCODE # · Invoke-ExchangeCommand → simpler remoting # · Open-RemoteRegistry → -hive LocalMachine -machine A0004915 -path SOFTWARE # #-- check $LASTEXITCODE from psexec invocations function Check-PSExecResult { Param($err=$LASTEXITCODE) switch ($err) { 0 { Write-Host -f Yellow "✔ okay" } 1 { Write-Host -f Yellow "✔ okay" } 2 { Write-Host -f Red "✘ failed" } default { Write-Host -f Gray "➗ Exitcode=$err" } } } #-- might as well define a PSExec wrapper then... function PSExec { [CmdletBinding()] Param($machine, [Parameter(ValueFromRemainingArguments=$true)]$cmd) $cmd = (($cmd | % { if ($_ -match ' ') { '"'+$_+'"' } else { $_ }}) -join ' ') #Write-Host -f DarkYellow "❏ PSExec($machine $cmd)" Invoke-Expression "& PSExec.exe $machine $cmd 2>&1" | Select -Skip 3 | Write-Host -b DarkGray # [void](Check-PSExecResult $LASTEXITCODE) } #-- default session (just started once, then kept in memory) function Import-ExchangeSession { $params = $cfg.exchange if (! (Test-Path function:Get-Mailbox)) { $null = ( Write-Host -f Green "❏ Exchange connection..." ) $global:Exchange_Session = New-PSSession @params $null = Import-PSSession -Session $global:Exchange_Session } else { $null = ( Write-Host -f DarkGray "✔ Exchange conn active." ) } } #-- via WMI _ComputerSystem or looking up Owner of Explorer _Process function Get-CurrentUser { Param($machine) if (($W = GWMI win32_computersystem -comp $machine) -and ($W.username)) { $r = $W.username } elseif ($W = GWMI Win32_Process -ComputerName $machine -filter "name='Explorer.exe'") { $r = ($w | % { $_.getOwner().user } | ? { $_ -notmatch "^SYSTEM$" }) if ($r -is [array]) { $r = $r[0] } } else { $r = "nobody" } return $r -replace "^\w+\\(?=\w+)","" } #-- via WMI _userAccount function Get-UserSID { Param($user) ([WMI]"win32_userAccount.Domain='$($cfg.domain)',Name='$user'").SID } #-- remote registry function Open-RemoteRegistry { <# .SYNOPSIS Open remote registry tree .DESCRIPTION Establish a Win32.Registry connection to remote machine. Does not retrieve Leafes/Values itself. .PARAMETER Path Can either be a full path such as "\\HOSTNAME\HKLM\SOFTWARE\Windows" Or just the regpath "SOFTWARE\Windows" when both -Hive and -Machine are given .PARAMETER Hive If no full -Path given, should name "HKLM", "HKCR", or "HKCU" (current user is looked up automatically). .PARAMETER Machine If no full -Path given, lists the remote hostname to connect to. .EXAMPLE $R = Open-RemoteRegisty "\\localhost\HKLM\SW\WindowsCurrentControlSet" $R = Open-RemoteRegisty -Machine "localhost" -Hive "HKLM" -Path "SW\WindowsCurrentControlSet" #> Param( $path = $null, # preferred: full specifier "\\HOSTNAME\HKLM\RegPath" (host+hive+path; no leaf/value) $hive = "LocalMachine", $machine = $null, $writemode = $true, [switch]$silent = $false ) #-- combine path if it starts with "\\" two backslashes if ((!$machine) -and ($path -match "^\\\\(\w+)\\(\w+)\\(.+)$")) { $machine = $matches[1] $hive = $matches[2] $path = $matches[3] } #-- hive aliases $hive = switch -regex ($hive) { ".*LM" { "LocalMachine" } ".*CR" { "ClassesRoot" } ".*CU" { $path = (Get-UserSID (Get-CurrentUser $machine)) + "\" + $path [Microsoft.Win32.RegistryHive]::Users } ".*USERS" { [Microsoft.Win32.RegistryHive]::Users } } if (!$hive) { $hive = "LocalMachine" } #-- open if (!$silent) { $null = ( Write-Host -f DarkGray "❏ Remote registry connection [$machine\$hive]..." ) } $R = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("$hive", $machine) if (!$silent) { $null = ( Write-Host -f DarkGray "❏ Open subkey [$path]..." ) } return $R.OpenSubKey($path, $writemode) } #-- shortcuts function Set-RemoteRegistry { Param($path = "\\localhost\HKLM\SOFTWARE\Etc\Key", $value="", $type="String") if ($path -match "^\\\\(\w+)\\(\w+)\\(.+)\\([^\\]+)$") { $R = Open-RemoteRegistry -machine $matches[1] -hive $matches[2] -path $matches[3] -Silent $R.setValue($matches[4], $value, $type) } } function Get-RemoteRegistry { Param($path = "\\localhost\HKLM\SOFTWARE\Etc\Key") if ($path -match "^\\\\(\w+)\\(\w+)\\(.+)\\([^\\]+)$") { $R = Open-RemoteRegistry -machine $matches[1] -hive $matches[2] -path $matches[3] -writemode $false -Silent return $R.getValue($matches[4]) } } |
Added tools/plugins/funcs_json.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # encoding: utf-8 # api: ps # title: JSON functions # description: JSON decoder for PS 2.0 # version: 0.4 # type: init # category: misc # hidden: 1 # priority: optional # # Defines: # · Convert-FromJSON20 #-- deserialize JSON to hashtable/array function Convert-FromJSON20 { Param($json) try { $void = [System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions") $parser = New-Object System.Web.Script.Serialization.JavaScriptSerializer #$obj = New-Object PSObject -Property $obj = $parser.DeserializeObject($json) } catch { $obj = @{} } return $obj } #-- converts nested hashtables/dictionaries/psobjects to string (visual tree indenting) function Out-Struct { Param($obj, $SPC = "") ForEach-KV $obj { Param($k,$v) if ($v -is [string] -or $v -is [int]) { "$SPC➜ $k = $v " } else { "$SPC➩ $k" Out-Struct $v " $SPC" } } } #-- iterate over dicts/objects/arrays using scriptblock with Param($k,$v) function ForEach-KV { Param($var, $cb, $i=0) switch ($var.GetType().Name) { Array { $var | % { $cb.Invoke($i++, $_) } } HashTable { $var.Keys | % { $cb.Invoke($_, $var[$_]) } } "Dictionary``2" { $var.Keys | % { $cb.Invoke($_, $var.Item($_)) } } PSobject { $var.GetIterator() | % { $cb.Invoke($_.Key, $_.Value) } } PSCustomObject { $var.GetIterator() | % { $cb.Invoke($_.Key, $_.Value) } } default { $cb.Invoke($i++, $_) } } } |
Added tools/plugins/funcs_remoting.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # api: multitool # version: 0.6 # title: Remoting funcs # description: Start-SchedTask, WMI ScheduledJob, PSRemoting # type: init # category: functions # status: TESTING # hidden: 0 # config: - # # Fail: # - psexec does not allow interactive apps in foreground even with -i and session id (from WMI/win32_process) # - Win32_ScheduledJob cannot be run immediately; requires latency timeing and timestamp crafting # # Working approach: # ❏ schtasks.exe /Create /S A0004915 /TN TeamViewer /RU DOMAIN\TestAccount # /IT /TR "'C:\Program Files\TeamViewerQS\Team ViewerQS-idcm7ct8bq.exe'" # /SC ONCE /SD 05/05/2022 /ST 23:59 /F # ❏ schtasks /run /S A0004915 /TN TeamViewer # #-- run as scheduled job # function Start-SchedTask { Param( $machine = "TESTHOST", $cmd = "'cmd.exe'", # Note the double string context quoting "'...'" for paths with spaces! $taskname = "psonce" ) #-- ping if (!(Test-Connection $machine -Quiet -ErrorAction SilentlyContinue)) { Write-Host -f Red " $machine offline" return } #-- current user $user = GWMI win32_ComputerSystem -ComputerName $machine | select -expand username Write-Host -f Yellow "❏ $user on $machine" #-- sched task Write-Host -f Yellow "❏ scheduling $cmd" schtasks.exe /Create /S $machine /TN $taskname /RU "$($cfg.domain)\$username" /IT /TR "$cmd" /SC ONCE /SD 05/05/2022 /ST 23:59 /F Write-Host -f Yellow "❏ starting task..." schtasks.exe /run /S $machine /TN $taskname } #-- check for session id (`QUSER` doesn't work) # function Get-RemoteSessionID { Param( $machine ) $sid = Get-WmiObject Win32_Process -ComputerName $machine -Filter 'Name="explorer.exe"' | Select -First 1 -Expand SessionId if ($sid) { return $sid } else { Write-Host -f Red "✘ No user desktop session found; assuming default 1" return 1 } } |
Added tools/plugins/init_env.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # api: ps # title: initialize $ENV # description: predefines some Powershell and system environment variables # version: 0.5 # type: init # category: misc # hidden: 1 # priority: core #-- powershell #$Debug = $true #$ErrorActionPreference = "SilentlyContinue" #$DebugPreference = "SilentlyContinue" #$ProgressPreference = "Continue" #$VerbosePreference = "SilentlyContinue" $WarningPreference = "Continue" #$WhatIfPreference = $False #$ConfirmPreference = "High" $OFS=" " #-- environment if (!$ENV:EDITOR) { $ENV:EDITOR = "notepad" } if (!$ENV:XDG_CONFIG_HOME) { $ENV:XDG_CONFIG_HOME = $ENV:APPDATA } #-- create config dir if (!(Test-Path ($cfg.user_plugins_d))) { md ($cfg.user_plugins_d) } |
Added tools/plugins/init_intro.ps1.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # api: ps # title: init screen # description: writes something on the Output window on startup # version: 0.0.3 # type: init-gui # category: misc # hidden: 1 # priority: core if ($GUI.w -and !$CLI) { # combine version of all scripts $sigma_ver = ($menu | % { $_.version } | ? { $_ -match "\d" } | % { $_ -replace "-.+$","" -replace "\D","" } | Measure -Sum).Sum -replace "(?<=\d)(?!$)","." # output Out-Gui -f Yellow -b "#223388" "ClickyColoury frontend to Multi-Tools Σ ≈ $sigma_ver" Out-Gui -f "#88bb22" " ✉ Color clipboard enabled (➤HTML icon)" } |
Added tools/plugins/menu_editscripts.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # api: multitool # type: init-gui # version: 0.5 # title: --- edit --- # description: edit multitool tools/ and main script, modules # hidden: 1 # category: edit # config: {} # # Adds to Config > Edit scripts menu if ((!$CLI) -and (!$e) -and ($GUI.w)) { $wm = $GUI.w.findName("Menu_EDIT").Items $submenus = @{} #-- prepend main scripts $add = @( @{fn = ".\modules\starter.ps1"} @{fn = ".\modules\wpf.psm1"}, @{fn = ".\modules\wpf.xaml"} @{fn = ".\modules\menu.psm1"} @{fn = ".\modules\clipboard.psm1"} ) #-- add edit entries ($add+$menu) | ? {$_.fn} | Sort-Object {$_.fn} | % { #-- dir and path if (($_fn = $_.fn) -match "([.\w]+)[\\//]([^\\//]+)$") { $dir = $matches[1] -replace "tools\.","" $fn = $matches[2] } else { continue } #-- find/add dir submenu if ($m = $submenus[$dir]) { } else { $m = W MenuItem @{Header=$dir} $submenus[$dir] = $m $wm.Add($m) } $m.Items.Add((W MenuItem @{Header="_$fn"; Add_click={notepad "$_fn"}.getnewclosure()})) } } |
Added tools/plugins/update_cache_adsearch.ps1.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # api: multitool # version: 0.1 # title: update ADSearch.txt # description: Update ADSearch cache file # type: inline # category: update # hidden: 1 # img: log # status: obsolete # # Update data\adsearch.txt # # - used by WPF multiTool user search # - is a plain line-wise text file of format: # "ADUser | Name, USer | +1-234-567890" # $cache_fn = ".\data\adsearch.txt" echo "Get-ADUser ... > $cache_fn" Get-ADUser -Filter * -Properties SAMAccountName,DisplayName,TelephoneNumber | % { "{0} | {1} | {2}" -f @($_.SAMAccountName, $_.DisplayName, $_.TelephoneNumber) } | Out-File $cache_fn -Encoding UTF8 |
Added tools/test/test_htmlclip.ps1.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | # api: multitool # title: test html clip # description: Set-clipboardHtml # version: 0.2 # category: test # type: inline # # testy test $email ="test@test" $user = "user123" Set-ClipboardHtml "<p><font style='color:green'>✔➟</font> The mailbox <<font style='color:blue'>$email</font>> for the account <b style='color:#553311'>$user</b> has been created.</p>" |
Added tools/test/test_ping.ps1.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | # api: multitool # type: inline # category: test # title: ping # description: localhost # version: 0.1 ping $machine |
Added tools/test/test_processes.ps1.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | # api: multitool # version: 0.1 # title: processes # description: lists local machine tasks # type: inline # category: test # hidden: 1 # # Dummy test script Get-Process |
Added tools/test/test_readhost.ps1.
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | # api: multitool # type: inline # category: test # title: Read-Host # description: runs the Ask-Gui() wrapper # version: 0.1 Read-Host "User" # should get the $username variable without prompt Read-Host "Else" # should pop up a message box |
Added tools/test/test_vars.ps1.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # api: multitool # title: test vars # description: $GUI.vars and Get-ParamVarsCmd interpol # version: 0.0 # param: machine, username, field1, field2 # category: test # type: window # # testy test param($mach=0, $user=0, $field1=0, $field2=0) Write-Host "host=$mach" Write-Host "user=$user" Write-Host "f1=$field1" Write-Host "f2=$field2" Read-host "Wait" |
Added tools/test/test_writehost.ps1.
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | # api: multitool # type: inline # category: test # title: Write-Host # description: test color output # version: 0.1 "Red,Green,Yellow,Black,Orange".split(",") | % { Write-Host $_ -f $_ } |
Added tools/wmi/diskspace.ps1.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # api: multitool # version: 0.1 # title: Disk space # description: List logicaldisks / free space # type: inline # category: wmi # hidden: 0 # key: w3|service|services # config: {} # # list services on remote machine Param($machine = (Read-Host "Machine")) Get-WmiObject win32_logicaldisk -computer $machine | FL |
Added tools/wmi/network.ps1.
> > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # api: multitool # version: 0.4 # title: Network adapters # description: Scans remote PC via WMI win32_networkadapters # type: inline # category: wmi # tag: proxy # hidden: 0 # key: w4|network|network-?adapters|ada?pt[ers]*|nwa # config: {} # # Useful for detecting parallel LAN and WLAN connections. # # → proxy issues # Param($machine = (Read-Host "Machine")); $adapters = Get-WMIObject win32_networkadapter -filter "netconnectionstatus=2" -ComputerName $machine Format-List -InputObject $adapters -Property NetConnectionID,Name,MACaddress,ServiceName,InterfaceIndex |
Added tools/wmi/sid.ps1.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # api: multitool # version: 1.0 # title: [WMI] $user # description: Show SID and other AD user properties # type: inline # category: wmi # hidden: 0 # key: w1|sid|wmi_user|userid # config: {} # # Get detailed user info (such as SID for AD name) via WMI query. Param($user = (Read-Host User)) [wmi] "win32_userAccount.Domain='$($cfg.domain)',Name='$user'" | Format-List -Prop * |