├── MM_Data ├── MM_Data.psm1 └── Ini.ps1 ├── MM_Desktop ├── Take-ScreenShot.ps1 ├── MM_Desktop.psm1 ├── Show-BalloonTip.ps1 └── Set-PinnedApplication.ps1 ├── MM_SVN ├── MM_SVN.psm1 ├── SVN-GetUsers.ps1 ├── SVN-Ignore.ps1 └── SVN-OnChange.ps1 ├── MM_Admin ├── MM_Admin.psm1 ├── Show-UserTasks.ps1 ├── Get-NICredential.ps1 ├── Get-ApplicationUninstallKey.ps1 ├── Get-CPULoad.ps1 ├── Open-Regedit.ps1 ├── Invoke-RemoteDesktop.ps1 ├── Invoke-Environment.ps1 ├── Set-AppKeyAliases.ps1 ├── Add-Path.ps1 ├── Set-EnvironmentVariable.ps1 ├── Register-Application.ps1 ├── Set-AutoLogon.ps1 ├── Register-LoginTask.ps1 ├── Start-ElevatedProcess.ps1 ├── Repair-Path.ps1 └── New-ProxyCommand.ps1 ├── MM_Sugar ├── MM_Sugar.psm1 ├── Exit-Powershell.ps1 ├── Enter-Parent.ps1 ├── README.md ├── Reset-Terminal.ps1 ├── Show-Groups.ps1 ├── Start-Powershell.ps1 ├── Set-WorkingDir.ps1 ├── Edit-Profile.ps1 ├── Get-ISO8601Time.ps1 ├── Test-Admin.ps1 ├── Expand-PoshString.ps1 ├── ClipboardDir.ps1 ├── Get-Weather.ps1 ├── Invoke-ControlPanelItem.ps1 ├── Edit-File.ps1 ├── Get-HelpPager.ps1 ├── Invoke-Inside.ps1 ├── Get-Sprunge.ps1 ├── Shutdown.ps1 ├── Wait-Action.ps1 ├── Add-ToProfile.ps1 ├── EventLogs.ps1 ├── Invoke-Repeater.ps1 ├── Test-Credential.ps1 └── Get-Choice.ps1 ├── MM_HashTables ├── MM_HashTables.psm1 ├── ConvertTo-HashTable.ps1 ├── Merge-Hashtables.ps1 ├── Write-HashTable.ps1 └── Edit-HashTable.ps1 ├── MM_FileSystem ├── MM_FileSystem.psm1 ├── Get-ShortPath.ps1 ├── Close-LockedFile.ps1 ├── Find-UpwardFile.ps1 ├── Start-FileSystemWatcher.ps1 ├── Get-FileEncoding.ps1 ├── Copy-ItemWithProgress.ps1 ├── Update-FileAssociation.ps1 └── New-SymLink.ps1 ├── MM_VPN ├── MM_VPN.psm1 ├── vpn.ps1 └── README.md ├── MM_TotalCmd ├── test.ps1 ├── Ini.ps1 └── totalcmd.ps1 ├── setup.ps1 ├── MM_Network ├── Get-Proxy.ps1 ├── Set-DNS.ps1 ├── MM_Network.psm1 ├── Base64.ps1 ├── Disconnect-WirelessNetwork.ps1 ├── Stop-ProcessByPort.ps1 ├── Set-UncHostnameAlias.ps1 ├── Get-WirelessNetwork.ps1 ├── Update-Proxy.ps1 ├── Update-CLIProxy.ps1 ├── Connect-WirelessNetwork.ps1 ├── Test-URI.ps1 └── Connect-Mstsc.ps1 ├── README.md └── LICENSE /MM_Data/MM_Data.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_Desktop/Take-ScreenShot.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/posh/HEAD/MM_Desktop/Take-ScreenShot.ps1 -------------------------------------------------------------------------------- /MM_SVN/MM_SVN.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_Admin/MM_Admin.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_Desktop/MM_Desktop.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_Sugar/MM_Sugar.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_HashTables/MM_HashTables.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | Export-ModuleMember -Function * -Alias * 3 | -------------------------------------------------------------------------------- /MM_Sugar/Exit-Powershell.ps1: -------------------------------------------------------------------------------- 1 | function Exit-Powershell { exit } 2 | 3 | sal e Exit-Powershell 4 | sal q Exit-Powershell 5 | -------------------------------------------------------------------------------- /MM_FileSystem/MM_FileSystem.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function * -Alias * 4 | -------------------------------------------------------------------------------- /MM_VPN/MM_VPN.psm1: -------------------------------------------------------------------------------- 1 | ls "$PSScriptRoot\*.ps1" | % { . $_ } 2 | 3 | Export-ModuleMember -Function connect-vpn, disconnect-vpn -Alias * 4 | -------------------------------------------------------------------------------- /MM_Sugar/Enter-Parent.ps1: -------------------------------------------------------------------------------- 1 | function Enter-Parent { cd .. } 2 | function Enter-GrandParent { cd ..\.. } 3 | sal .. Enter-Parent 4 | sal ... Enter-GrandParent 5 | 6 | -------------------------------------------------------------------------------- /MM_Sugar/README.md: -------------------------------------------------------------------------------- 1 | 2 | MM_Sugar 3 | ======== 4 | 5 | This Powershell module contains syntax sugars in order to ease every day work in console. See man for each function. 6 | 7 | -------------------------------------------------------------------------------- /MM_Sugar/Reset-Terminal.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Reset terminal colors and clear the screen 4 | #> 5 | function Reset-Terminal() { 6 | [Console]::ResetColor() 7 | cls 8 | } 9 | -------------------------------------------------------------------------------- /MM_TotalCmd/test.ps1: -------------------------------------------------------------------------------- 1 | ls $PSScriptRoot\*.ps1 -Exclude test.ps1 | % { . $_ } 2 | 3 | #Install-TCPlugin $PSScriptRoot\..\uninstaller64_1.0.1.rar Uninstaller64 4 | #Uninstall-TCPlugin Uninstaller64 5 | -------------------------------------------------------------------------------- /MM_Sugar/Show-Groups.ps1: -------------------------------------------------------------------------------- 1 | function Show-Groups { 2 | [System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | % {$_.Translate([Security.Principal.NTAccount])} | sort 3 | } 4 | sal groups Show-Groups 5 | -------------------------------------------------------------------------------- /MM_Sugar/Start-Powershell.ps1: -------------------------------------------------------------------------------- 1 | function Start-Powershell([switch]$As) { 2 | $cmd = 'start powershell' 3 | if ($RunAs) { $cmd += ' -Verb RunAs' } 4 | iex $cmd 5 | } 6 | Set-Alias posh Start-Powershell 7 | -------------------------------------------------------------------------------- /MM_Sugar/Set-WorkingDir.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Set current and working directory to the same one. 3 | #> 4 | function Set-WorkingDir([string] $Dir=$pwd) { cd $Dir; [IO.Directory]::SetCurrentDirectory($Dir) } 5 | sal wcd Set-WorkingDir 6 | -------------------------------------------------------------------------------- /MM_Sugar/Edit-Profile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Edit $PROFILE using $Env:EDITOR 4 | #> 5 | function Edit-Profile { 6 | mkdir (Split-Path $PROFILE) -force -ea 0 | out-null 7 | ed $PROFILE 8 | } 9 | 10 | sal edp Edit-Profile 11 | -------------------------------------------------------------------------------- /MM_Admin/Show-UserTasks.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Returns all scheduled tasks under the username Path in Task Scheduler 3 | #> 4 | function Show-UserTasks() { Get-ScheduledTask -TaskPath "\$Env:USERDOMAIN\$Env:USERNAME\*" | sort State} 5 | sal stasks Show-UserTasks 6 | -------------------------------------------------------------------------------- /MM_Sugar/Get-ISO8601Time.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Get current time in format ISO8601 yyyy-MM-ddTHH-mm-ss with : of time replaced with - 3 | so it can be used with Windows filenames 4 | #> 5 | function Get-ISO8601Time([switch]$fs) {[DateTime]::Now.ToString("s").Replace(':','-')} 6 | sal now Get-ISO8601Time 7 | -------------------------------------------------------------------------------- /MM_Sugar/Test-Admin.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Test for administration privileges 3 | #> 4 | function Test-Admin() { 5 | $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() 6 | $usercontext.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 7 | } 8 | -------------------------------------------------------------------------------- /MM_Sugar/Expand-PoshString.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Expands powershell string. 3 | .EXAMPLE 4 | gc template.ps1 | expand 5 | #> 6 | function Expand-PoshString() { 7 | [CmdletBinding()] 8 | param ( [parameter(ValueFromPipeline = $true)] [string] $str) 9 | 10 | "@`"`n$str`n`"@" | iex 11 | } 12 | 13 | sal expand Expand-PoshString 14 | -------------------------------------------------------------------------------- /setup.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 3 2 | 3 | Write-Host "Adding majkinetor's PowerShell modules to user $PROFILE" 4 | mkdir -force (Split-Path -Parent $PROFILE) | out-null 5 | if ($Env:PSModulePath -like "*$PSScriptRoot*") { Write-Host 'Modules are already registered, exiting'; return } 6 | "`$Env:PSModulePath += `";$PSScriptRoot`"" | Out-File -Encoding ascii -Append $PROFILE -------------------------------------------------------------------------------- /MM_Sugar/ClipboardDir.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | 3 | function Set-ClipboardDir() { "$pwd" | Set-Clipboard } 4 | function Get-ClipboardDir() { 5 | $item = Get-Clipboard 6 | if (!(Test-Path $item)) { throw "Clipboard item is not a valid path" } 7 | 8 | if (!(gi $item).PSIsContainer) { $item = Split-Path $item } 9 | pushd $item 10 | } 11 | 12 | sal cpushd Set-ClipboardDir 13 | sal cpopd Get-ClipboardDir 14 | -------------------------------------------------------------------------------- /MM_Sugar/Get-Weather.ps1: -------------------------------------------------------------------------------- 1 | <# .SYNOPSIS 2 | Retreves weather using wttr.in 3 | 4 | .EXAMPLE 5 | wtr 6 | #> 7 | function Get-Weather($City=$Env:City) { 8 | if ($City -eq $null) { 9 | $Env:City = $City = Read-Host "Specify city" 10 | [Environment]::SetEnvironmentVariable("City", $City, "User") 11 | } 12 | (Invoke-WebRequest http://wttr.in -UserAgent curl -UseBasicParsing).Content 13 | } 14 | 15 | sal wtr Get-Weather 16 | -------------------------------------------------------------------------------- /MM_Sugar/Invoke-ControlPanelItem.ps1: -------------------------------------------------------------------------------- 1 | #require -version 3 2 | 3 | <# 4 | .SYNOPSIS 5 | Invoke control papnel item by filtering out the item list with fuzzy search 6 | #> 7 | function Invoke-ControlPanelItem() { 8 | if (!(gcm fzf -ea 0)) { Write-Host "To use this function, install fzf first: cinst fzf"; return } 9 | Get-ControlPanelItem | % name | sort | fzf --reverse | Show-ControlPanelItem 10 | } 11 | 12 | sal control Invoke-ControlPanelItem -------------------------------------------------------------------------------- /MM_Network/Get-Proxy.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 22-Feb-2016. 3 | 4 | #requires -version 3 5 | 6 | <# 7 | .SYNOPSIS 8 | Detect proxy for the url 9 | #> 10 | 11 | function Get-Proxy( [string]$Url = 'http://www.google.com' ) { 12 | $client = New-Object System.Net.WebClient 13 | if ($client.Proxy.IsBypassed($Url)) { return $null } 14 | $proxyAddr = $client.Proxy.GetProxy($url).Authority 15 | return $proxyAddr 16 | } 17 | -------------------------------------------------------------------------------- /MM_Network/Set-DNS.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Set the DNS list for the interface 4 | 5 | .EXAMPLE 6 | PS> Set-dns 10.32.34.192 @('8.8.8.8','4.4.4.4') 7 | 8 | Set the primary and secundary DNS for the interface with IP address 10.32.34.192 9 | #> 10 | function Set-Dns( [string]$ip, [string[]] $dnsList) { 11 | $Interface = Get-WmiObject Win32_NetworkAdapterConfiguration | ? { $_.IpAddress -eq $ip } 12 | $Interface.SetDNSServerSearchOrder($dnsList) 13 | } 14 | 15 | -------------------------------------------------------------------------------- /MM_FileSystem/Get-ShortPath.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Get the short path (8.3) of the file path 4 | .EXAMPLE 5 | ls $Env:ProgramFiles | Get-ShortPath 6 | #> 7 | function Get-ShortPath 8 | { 9 | BEGIN { 10 | $fso = New-Object -ComObject Scripting.FileSystemObject 11 | } 12 | PROCESS { 13 | $f = $_ 14 | if ($f.psiscontainer) { $fso.getfolder($f.fullname).ShortPath } 15 | else { $fso.getfile($f.fullname).ShortPath } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MM_Network/MM_Network.psm1: -------------------------------------------------------------------------------- 1 | # Export functions that start with capital letter, others are private 2 | # Include file names that start with capital letters, ignore other 3 | 4 | $pre = ls Function:\* 5 | ls "$PSScriptRoot\*.ps1" | ? { $_.Name -cmatch '^[A-Z]+' } | % { . $_ } 6 | $post = ls Function:\* 7 | $funcs = compare $pre $post | select -Expand InputObject | select -Expand Name 8 | $funcs | ? { $_ -cmatch '^[A-Z]+'} | % { Export-ModuleMember -Function $_ } 9 | 10 | Export-ModuleMember -Alias * 11 | -------------------------------------------------------------------------------- /MM_Sugar/Edit-File.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | Edit multiple files in $Env:EDITOR 5 | 6 | .EXAMPLE 7 | PS> dir ..\*.txt | ed "my file.txt" 8 | 9 | Edit all text files from the parent directory along with "my file.txt" from current dir. 10 | #> 11 | function Edit-File () { 12 | $f = $input + $args | % { """$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $_ ))""" } 13 | iex ". '$Env:EDITOR' $f" 14 | } 15 | 16 | 17 | sal ed Edit-File 18 | sal edit Edit-File 19 | -------------------------------------------------------------------------------- /MM_Admin/Get-NICredential.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 27-Apr-2016. 3 | Author: M. Milic 4 | 5 | .SYNOPSIS 6 | Get Non-Interactive (NI) credentials 7 | #> 8 | function Get-NICredential([string]$Username, [string]$Password) 9 | { 10 | $ss = New-Object SecureString 11 | $Password.ToCharArray() | % { $ss.AppendChar($_) } 12 | New-Object PSCredential -Argumentlist $Username, $ss 13 | 14 | # [pscredential]::new($Username, ($Password | ConvertTo-SecureString -AsPlainText)) 15 | } 16 | -------------------------------------------------------------------------------- /MM_Sugar/Get-HelpPager.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 30-Mar-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get full help and display it in a pager (less.exe) 7 | 8 | .EXAMPLE 9 | PS> m gcm 10 | 11 | Get full help for gcm command 12 | 13 | .NOTE 14 | Depends on less. Install it via chocolatey: cinst less 15 | #> 16 | 17 | function Get-HelpPager() { 18 | if (!(gcm less.exe -ea 0)) { throw "Please install less: cinst less" } 19 | 20 | get-help "$args" -Full | less.exe 21 | } 22 | -------------------------------------------------------------------------------- /MM_Sugar/Invoke-Inside.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Execute script in given directory or given files directory 4 | .EXAMPLE 5 | PS> in c:\windows ls 6 | 7 | Executes the ls command in the given directory without changing the current directory. 8 | 9 | .EXAMPLE 10 | PS> in c:\windows\notepad.exe ls 11 | 12 | If the directory is a file, the script will execute within its parent. 13 | #> 14 | function Invoke-Inside ($dir, $script) { 15 | if (Test-Path $dir -PathType Leaf) { $dir = Split-Path $dir -Parent } 16 | pushd $dir; & $script; popd 17 | } 18 | 19 | sal inside Invoke-Inside 20 | -------------------------------------------------------------------------------- /MM_Network/Base64.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Base64String( [string] $s) { [Convert]::ToBase64String( [System.Text.Encoding]::Utf8.GetBytes($s)) } 2 | function ConvertFrom-Base64String( [string] $s) { [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($s))} 3 | 4 | <# 5 | .SYNOPSIS 6 | Convert from / to Base64 string 7 | 8 | .EXAMPLE 9 | 'foo bar' | b64 | % { Write-Host $_; $_ } | b64 -From 10 | 11 | Encode string 'foo bar' to Base64 form, dispaly it, and decode it. 12 | 13 | #> 14 | function b64 ( [Parameter(ValueFromPipeline=$true)] [string] $s, [switch]$From) { if ($From) { ConvertFrom-Base64String $s } else { ConvertTo-Base64String $s} } -------------------------------------------------------------------------------- /MM_Network/Disconnect-WirelessNetwork.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 25-Jun-2015. 3 | 4 | #requires -version 3 5 | 6 | <# 7 | .SYNOPSIS 8 | Disconnect from wireless network interface and eventually remove profile for given SSID 9 | #> 10 | function Disconnect-WirelessNetwork() { 11 | [CmdletBinding()] 12 | param( 13 | # Wireless network name 14 | [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 15 | [string]$SSID, 16 | 17 | # Force profile removal 18 | [switch]$Remove 19 | ) 20 | netsh wlan disconnect 21 | if ($Remove) { netsh wlan delete profile name=$SSID } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /MM_SVN/SVN-GetUsers.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 24-Mar-2015. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get the array of SVN users 7 | .EXAMPLES 8 | SVN-GetUsers 'http:\\svn\projectx' -Exclude 'VisualSVN Server','jenkins' 9 | 10 | Get the list of users but exclude application accounts. 11 | #> 12 | function SVN-GetUsers { 13 | [CmdletBinding()] 14 | param( 15 | # SVN Repository path 16 | [string] $Repository, 17 | # Names to exclude from the list 18 | [string[]] $Exclude 19 | ) 20 | svn log $Repository | select-string "^r[0-9]" | 21 | % { $_.ToString().Split("|")[1] } | sort -Unique | % { $_.Trim() } | ? { $Exclude -notcontains $_ } 22 | } 23 | -------------------------------------------------------------------------------- /MM_Admin/Get-ApplicationUninstallKey.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 10-Feb-2016. 3 | Author: M. Milic 4 | 5 | .SYNOPSIS 6 | Get the uninstall registry key of the application. 7 | 8 | .PARAMETER AppName 9 | String that is contained within DisplayName property. 10 | 11 | .RETURNS 12 | Registry key or null 13 | 14 | #> 15 | function Get-ApplicationUninstallKey([string]$AppName) 16 | { 17 | $local_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' 18 | $machine_key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' 19 | $machine_key6432 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' 20 | gp @($machine_key6432, $machine_key, $local_key) -ea 0 | ? { $_.DisplayName -like "*$AppName*" } 21 | } 22 | -------------------------------------------------------------------------------- /MM_Admin/Get-CPULoad.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | .SYNOPSIS 4 | List top process relative to CPU load. 5 | #> 6 | function Get-CPULoad([switch]$NoLoop, [int]$Top=20, [string]$Computer) { 7 | 8 | $cpu = { Get-Counter '\Process(*)\% Processor Time' -ea 0 |% countersamples | select -Property instancename, cookedvalue | 9 | sort CookedValue -Descending | select -First $using:Top | 10 | ft @{L='Process'; E={$_.InstanceName.PadRight(30)}}, @{L='CPU'; E={($_.Cookedvalue/100).toString('P')}} 11 | } 12 | 13 | if ($NoLoop) { . $cpu; return } 14 | rjb top -ea 0 15 | while($true) { 16 | Start-Job -Name top -ScriptBlock $cpu | Out-Null 17 | Wait-Job top | Out-Null 18 | cls 19 | Receive-Job top 20 | rjb top 21 | } 22 | } 23 | sal top Get-CpuLoad 24 | 25 | -------------------------------------------------------------------------------- /MM_Sugar/Get-Sprunge.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 30-Mar-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | The client for the sprunge.us that lets you paste stuff online easily 7 | 8 | .EXAMPLE 9 | PS> ls | sprunge 10 | 11 | Send content of the directory 12 | 13 | .EXAMPLE 14 | PS> cat file.ps1 | sprunge ps1 -Open 15 | 16 | Send content of the file, highlight powershell and open it in the browser 17 | #> 18 | function Get-Sprunge([string]$FileType=".", [switch]$Open) { 19 | if (!(gcm curl.exe -ea 0)) { throw 'Sprunge requires curl. Use cinst curl.exe' } 20 | $url = $input | curl.exe -s -F 'sprunge=<-' -H "Expect: " http://sprunge.us 21 | $url += "?$FileType" 22 | $url | clip 23 | if ($Open) { start $url } 24 | $url 25 | } 26 | 27 | sal sprunge get-sprunge 28 | sal spaste get-sprunge 29 | -------------------------------------------------------------------------------- /MM_Sugar/Shutdown.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-Shutdown { shutdown.exe /t 0 /s} 2 | function Invoke-Reboot { shutdown.exe /t 0 /r } 3 | function Invoke-Logout { logoff.exe } 4 | 5 | function Invoke-Hibernation { 6 | $enabled = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Power -name HibernateEnabled -ea 0 | select -Expand HibernateEnabled 7 | if (!$enabled) { 8 | Write-Warning 'Hibernation doesn''t seem to be enabled on this system.' 9 | Write-Warning 'To enable it, run elevated: powercfg.exe /H ON' 10 | Write-Warning 'Alternativelly, go to `Control Panel\Hardware and Sound\Power Options\System Settings` to enable it.' 11 | } 12 | 13 | shutdown.exe /h 14 | } 15 | 16 | 17 | sal halt Invoke-Shutdown 18 | sal suspend Invoke-Hibernation 19 | sal hibernate Invoke-Hibernation 20 | sal reboot Invoke-Reboot 21 | sal logout Invoke-Logout 22 | sal logoff Invoke-Logout 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Posh modules and functions by majkinetor 3 | 4 | This repository contains number of PowerShell modules I develop for various needs during my work on number of other projects. Every module is well documented and intented to work on any computer having adequate version of PowerShell. 5 | 6 | ## Install 7 | 8 | 9 | ```Powershell 10 | git clone https://github.com/majkinetor/posh 11 | ./posh/setup.ps1 12 | ``` 13 | 14 | To import-module from SMB share without security warning: 15 | 16 | ```Powershell 17 | $r = "HKCU:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap" 18 | mkdir -force $r 19 | sp $r UNCAsIntranet 1 20 | ``` 21 | 22 | ## Discover 23 | 24 | To discover available functions from the CLI run `Get-Command`: 25 | 26 | gcm -module mm_network 27 | 28 | To get help about specific function from the CLI run `Get-Help`: 29 | 30 | import-module mm_network 31 | man proxy -------------------------------------------------------------------------------- /MM_Admin/Open-Regedit.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 17-Mar-2017. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Open regedit at given location 9 | 10 | .EXAMPLE 11 | PS HKLM:\SOFTWARE\> gi . | Open-Regedit 12 | 13 | Open regedit at HKLM:\Software by value from pipeline 14 | 15 | .EXAMPLE 16 | PS HKLM:\SOFTWARE\> Open-Regedit $pwd 17 | 18 | Open regedit at HKLM:\Software via argument 19 | 20 | .NOTES 21 | Original by http://goo.gl/5aQgtd 22 | 23 | #> 24 | function Open-Regedit { 25 | 26 | [cmdletbinding()] 27 | param( 28 | [Parameter(ValueFromPipeLine=$true, ValueFromPipeLineByPropertyName=$true)] 29 | [Alias('Name')] 30 | [string]$Key 31 | ) 32 | 33 | $Key = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Key) 34 | Write-Verbose "Open $Key" 35 | sp HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit -Name LastKey -Value $Key -Force 36 | regedit -m # Open new instance 37 | } 38 | 39 | Set-Alias regjump Open-Regedit 40 | -------------------------------------------------------------------------------- /MM_SVN/SVN-Ignore.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 20-Feb-2015. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get, add or remove files to the svn ignore list for the current directory. 7 | 8 | .PARAMETER Remove 9 | Set to remove files instead of adding them. 10 | 11 | .EXAMPLE 12 | svn-ignore file1 file2 glob? *.log 13 | #> 14 | function svn-ignore([switch]$Remove) 15 | { 16 | $OFS="`n" 17 | $ignored = svn propget svn:ignore 18 | 19 | if ($args.Length -ne 0) { 20 | if (!$Remove) 21 | { 22 | $files = $args -join "`n" 23 | $files = $files | ? {$ignored -notcontains $_} 24 | $ignored = "${ignored}${files}".Trim() 25 | } else { 26 | $args | % { $ignored = $ignored -replace [Regex]::Escape($_),'' } 27 | $ignored = $ignored -split "`n" | ? { $_.Trim() -ne '' } 28 | } 29 | 30 | svn propset svn:ignore "$ignored" . 31 | } 32 | 33 | $out = ($ignored -split "`n") 34 | "Currently ignored in this dir ($($out.Length)):`n`n" 35 | $out 36 | } 37 | -------------------------------------------------------------------------------- /MM_Desktop/Show-BalloonTip.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Show balloon tooltip 4 | 5 | .DESCRIPTION 6 | Show balloon tooltip from system tray 7 | 8 | .EXAMPLE 9 | Show-BalloonTip -Title Website -MessageType Info -Message 'Build OK!' 10 | #> 11 | function Show-BalloonTip { 12 | [cmdletbinding()] 13 | param( 14 | [parameter(Mandatory=$true)] 15 | [string]$Title, 16 | 17 | [ValidateSet("Info","Warning","Error")] 18 | [string]$MessageType = "Info", 19 | 20 | [parameter(Mandatory=$true)] 21 | [string]$Message 22 | ) 23 | 24 | [system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null 25 | $balloon = New-Object System.Windows.Forms.NotifyIcon 26 | $path = Get-Process -id $pid | Select-Object -ExpandProperty Path 27 | $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) 28 | $balloon.Icon = $icon 29 | $balloon.BalloonTipIcon = $MessageType 30 | $balloon.BalloonTipText = $Message 31 | $balloon.BalloonTipTitle = $Title 32 | $balloon.Visible = $true 33 | $balloon.ShowBalloonTip(0) 34 | $balloon.Dispose() 35 | } 36 | -------------------------------------------------------------------------------- /MM_Admin/Invoke-RemoteDesktop.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 15-Mar-2017. 3 | 4 | #requires -version 2 5 | 6 | <# 7 | .SYNOPSIS 8 | Invokes remote desktop from command line and remembers credentials. 9 | .NOTES 10 | Might not work with complain about identity of the server: 11 | Your system administrator does not allow the use of saved credentials to log on to remote computer because identity is not fully verified 12 | http://serverfault.com/questions/396722/your-system-administrator-does-not-allow-the-use-of-saved-credentials-to-log-on 13 | #> 14 | function Invoke-RemoteDesktop { 15 | [CmdletBinding()] 16 | param( 17 | [string]$Server, 18 | [string]$User, 19 | [string]$Password, 20 | [switch]$Admin, 21 | [switch]$Fullscreen 22 | ) 23 | 24 | cmdkey /generic:$Server /user:$User /pass:$Password 25 | 26 | $params = @("/v:$Server") 27 | if ($Admin) { $params += '/admin' } 28 | if ($Fullscreen) { $params += '/f' } 29 | $cmd = "mstsc.exe $params" 30 | 31 | Write-Verbose $cmd 32 | iex $cmd 33 | } 34 | 35 | sal rdc Invoke-RemoteDesktop 36 | -------------------------------------------------------------------------------- /MM_Sugar/Wait-Action.ps1: -------------------------------------------------------------------------------- 1 | # $Url = 'https:/...' 2 | # Wait-Action { Invoke-WebRequest $args[0] } -ArgumentList $Url -Timeout 60 -Message "Testing service response: $url" 3 | # 4 | function Wait-Action ([ScriptBlock]$Action, [int]$Timeout=20, [string] $Message) { 5 | Write-Host "Waiting for action to succeed up to $Timeout seconds" 6 | Write-Host "|== $Message" 7 | 8 | $start = Get-Date 9 | while ($true) { 10 | $elapsed = ((Get-Date) - $start).TotalSeconds 11 | if ($elapsed -ge $Timeout) {break} 12 | 13 | $j = Start-Job $Action 14 | $maxWait = [int]($Timeout-$elapsed) 15 | if ($maxWait -lt 1) { $maxWait = 1 } 16 | Wait-Job $j -Timeout $maxWait | Out-Null 17 | 18 | if ($j.State -eq 'Running') { $err = 'still running'; break } 19 | if ($j0 = $j.ChildJobs[0]) { 20 | if ($err = $j0.Error) { continue } 21 | if ($err = $J0.JobStateInfo.Reason) {continue} 22 | } 23 | try { Receive-Job $j -ErrorAction STOP | Out-Null; } catch { $err = "$_"; continue } 24 | 25 | Write-Host "Action succeded"; return 26 | } 27 | 28 | throw "Action timedout. Last error: $err" 29 | } 30 | -------------------------------------------------------------------------------- /MM_Sugar/Add-ToProfile.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Aug-2015. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Add Powershell code to the Powershell profile 9 | 10 | .DESCRIPTION 11 | This function implements idempotent way to add a Powershell code to the $PROFILE. 12 | It will create $PROFILE if it doesn't exist and add the code if its not already there. 13 | 14 | .RETURNS 15 | True if code was added to the $PROFILE, False if $code is already in the $PROFILE. 16 | 17 | .EXAMPLE 18 | Add-ToProfile "Import-Module PSReadLine" 19 | #> 20 | function Add-ToProfile() { 21 | [CmdletBinding()] 22 | param( 23 | # Powershell code to add to the $PROFILE 24 | [string]$Code, 25 | # Specify to prepend code. Otherwise code wiill be appended 26 | [switch]$Prepend 27 | ) 28 | 29 | $prof = gc $PROFILE -ea ignore 30 | 31 | if ($prof -like "*$code*") { return $false } 32 | 33 | $p = .{ 34 | if ($Prepend) {$Code} 35 | $prof 36 | if (!$Prepend) {$Code} 37 | } 38 | 39 | mkdir -Force (Split-Path -Parent $PROFILE) | out-null 40 | sc $PROFILE $p 41 | 42 | return $true 43 | } 44 | -------------------------------------------------------------------------------- /MM_Sugar/EventLogs.ps1: -------------------------------------------------------------------------------- 1 | #require -version 3.0 2 | 3 | <# 4 | .SYNOPSIS 5 | Clear all event logs 6 | #> 7 | function Clear-EventLogs { 8 | Get-EventLog * | % { Clear-EventLog $_.Log } 9 | 10 | #Clear this one again as it accumulates clearing events from previous step 11 | Clear-EventLog System 12 | Get-EventLog * 13 | } 14 | 15 | <# 16 | .SYNOPSIS 17 | Get latest event logs across all event logs 18 | .Example 19 | logs Error,Warning -Newest 5 20 | #> 21 | function Get-EventLogs{ 22 | param( 23 | [ValidateSet('Error', 'Information', 'Warning', '*')] 24 | [string[]] $EntryType = 'Error', 25 | 26 | [int] $Newest=1000, 27 | 28 | [switch] $Raw 29 | ) 30 | $r = @() 31 | 32 | if ($EntryType -eq '*') { $EntryType = 'Error', 'Information', 'Warning' } 33 | Get-EventLog * | % Log | % { 34 | $log = $_ 35 | try { 36 | $r += Get-EventLog -Log $log -Newest $Newest -EntryType $EntryType -ea 0 37 | } 38 | catch { Write-Warning "$log - $_" } 39 | } 40 | $r = $r | sort TimeWritten -Descending 41 | if ($Raw) {$r} else { $r | select Source, TimeWritten, Message } 42 | } 43 | 44 | sal logs Get-EventLogs 45 | sal clearlogs Clear-EventLogs 46 | -------------------------------------------------------------------------------- /MM_Sugar/Invoke-Repeater.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Aug-2015. 3 | 4 | <# 5 | .SYNOPSIS 6 | Invoke script multiple times 7 | 8 | .EXAMPLE 9 | Invoke-Repeater { Remove-Item $dir } -Message "Removing files" 10 | 11 | .EXAMPLE 12 | Invoke-Repeater { Remove-Item $dir } -Message "Removing files" -FailureScript { Write-Warning "Failed to remove, ignoring" } 13 | #> 14 | function Invoke-Repeater([ScriptBlock] $ScriptBlock, $Message, [int] $MaxCount=3, [int] $Sleep = 5, [ScriptBlock] $FailureScript ) 15 | { 16 | Write-Host $Message 17 | for ($i = 1; $i -le $MaxCount; $i++) { 18 | try { 19 | & $ScriptBlock 20 | break 21 | } catch { 22 | Write-Host " failed executing script $i/$MaxCount" 23 | $errMessage = $_.Exception.Message -split "`n" 24 | $errMessage | % { Write-Host " " $_ } 25 | 26 | if ($i -eq $MaxCount) { 27 | if (!$FailureScript) { throw $_ } else { 28 | & $FailureScript $_ 29 | break 30 | } 31 | } 32 | Write-Host " trying again in $Sleep seconds`n"; Start-Sleep $Sleep 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MM_Network/Stop-ProcessByPort.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Kill process using a given network port 4 | 5 | .DESCRIPTION 6 | The function uses netstat to find a process using given port and kill it. 7 | #> 8 | function Stop-ProcessByPort( [ValidateNotNullOrEmpty()] [int] $Port ) { 9 | $netstat = netstat.exe -ano | select -Skip 4 10 | $p_line = $netstat | ? { $p = (-split $_ | select -Index 1) -split ':' | select -Last 1; $p -eq $Port } | select -First 1 11 | if (!$p_line) { Write-Host "No process found using port" $Port; return } 12 | $p_id = $p_line -split '\s+' | select -Last 1 13 | if (!$p_id) { throw "Can't parse process id for port $Port" } 14 | if ($p_id -eq '0') { Write-Warning $p_line; return } 15 | 16 | $proc = Get-CimInstance win32_process -Filter "ProcessId = $p_id" 17 | if (!$proc) { Write-Host "Process with pid $p_id using port $Port is no longer running" } 18 | Invoke-CimMethod $proc -MethodName "Terminate" | Out-Null 19 | 20 | #$p_name = ps -Id $p_id -ea 0 | kill -Force -PassThru | % ProcessName 21 | Write-Host "Process killed: $($proc.Name) (id $p_id) using port" $Port 22 | Write-Host " " $proc.Path 23 | Write-Host " " $proc.CommandLine 24 | } 25 | 26 | # sal killp Stop-ProcessByPort 27 | 28 | # killp 3001 -------------------------------------------------------------------------------- /MM_Network/Set-UncHostnameAlias.ps1: -------------------------------------------------------------------------------- 1 | function Set-UncHostnameAlias($UncPath, $RemoteAlias) { 2 | if (!(Get-Module -ListAvailable PsHosts)) { throw "PsHosts module must be installed. Install-Module PsHosts" } 3 | if (!($RemoteAlias)) { 4 | if (!$MyInvocation.ScriptName) { throw "Either run this function within a script or provide RemoteAlias parameter" } 5 | $RemoteAlias = (Split-Path -Leaf $MyInvocation.ScriptName) -replace '.ps1$' 6 | } 7 | 8 | $remote_hostname = $UncPath -split '\\' | ? {$_} | select -First 1 9 | 10 | $hostEntry = Get-HostEntry $RemoteAlias* -ea 0 | ? { $_.Comment -eq $remote_hostname } | select -First 1 11 | if (!$hostEntry) { 12 | $RemoteAlias += (Get-HostEntry $RemoteAlias*).Count + 1 13 | Write-Verbose "Adding alias $RemoteAlias => $remote_hostname" 14 | $remote_ip = Test-Connection -ComputerName $remote_hostname -Count 1 -ErrorAction Stop | % IPV4Address | % IPAddressToString 15 | Add-HostEntry -Name $RemoteAlias -Address $remote_ip -Force -Comment $remote_hostname | Out-Null 16 | } else { 17 | $RemoteAlias = $hostEntry.Name 18 | Write-Verbose "Using $remote_hostname alias: $RemoteAlias" 19 | } 20 | 21 | $UncPath.Replace("\\$remote_hostname", "\\$RemoteAlias") 22 | } 23 | -------------------------------------------------------------------------------- /MM_Admin/Invoke-Environment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Invokes a command and imports its environment variables. 4 | 5 | .DESCRIPTION 6 | It invokes any cmd shell command (normally a configuration batch file) and 7 | imports its environment variables to the calling process. Command output is 8 | discarded completely. It fails if the command exit code is not 0. To ignore 9 | the exit code use the 'call' command. 10 | 11 | .EXAMPLE 12 | # Invokes Config.bat in the current directory or the system path 13 | Invoke-Environment Config.bat 14 | 15 | .EXAMPLE 16 | # Visual Studio environment: works even if exit code is not 0 17 | Invoke-Environment 'call "%VS100COMNTOOLS%\vsvars32.bat"' 18 | 19 | .EXAMPLE 20 | # This command fails if vsvars32.bat exit code is not 0 21 | Invoke-Environment '"%VS100COMNTOOLS%\vsvars32.bat"' 22 | #> 23 | function Invoke-Environment { 24 | param 25 | ( 26 | # Any cmd shell command, normally a configuration batch file. 27 | [Parameter(Mandatory=$true)] 28 | [string] $Command 29 | ) 30 | 31 | $Command = "`"" + $Command + "`"" 32 | cmd /c "$Command > nul 2>&1 && set" | . { process { 33 | if ($_ -match '^([^=]+)=(.*)') { 34 | [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2]) 35 | } 36 | }} 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /MM_Admin/Set-AppKeyAliases.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 2016-01-21. 3 | Author: M. Milic 4 | 5 | .SYNOPSIS 6 | Creates Powershell aliases for executables listed in the App Paths registry key 7 | 8 | .EXAMPLE 9 | Set-AppKeyAliases *> $null 10 | 11 | Use this in $profile to set aliases for new shell 12 | 13 | .NOTES 14 | https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths 15 | #> 16 | function Set-AppKeyAliases(){ 17 | [CmdletBinding()] 18 | param() 19 | 20 | $AppPathKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" 21 | 22 | ls "HKLM:\$AppPathKey", "HKCU:\$AppPathKey" -ea 0 | % { 23 | $name = $_.Name -split '\\' | select -Last 1 24 | $name = $name -replace '.exe$' 25 | 26 | $path = gp $_.PSPath | select -expand '(default)' -ea 0 27 | if ($path -ne $null -and $path.StartsWith('"')) { $path = $path.Substring(1, $path.Length-2) } 28 | if ($path -eq $null -or !(Test-Path $path)) {Write-Warning "Ignoring invalid path for '$name' : '$path'"; return } 29 | 30 | Write-Verbose ("{0} = {1}" -f $name, $path) 31 | Set-Alias $name $path -Scope global -ea 0 -ErrorVariable err 32 | if ($err[0].FullyQualifiedErrorId -eq 'AliasNotWritable,Microsoft.PowerShell.Commands.SetAliasCommand') { Write-Warning "Alias not writable: $name" } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MM_FileSystem/Close-LockedFile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 19-Jun-2015. 3 | Modified: M. Milic 4 | 5 | .SYNOPSIS 6 | Close file locked by the application 7 | .NOTE 8 | Original: http://stackoverflow.com/a/30744517/82660 9 | #> 10 | function Close-LockedFile { 11 | Param( 12 | [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 13 | [String[]]$Filename 14 | ) 15 | Begin { 16 | $HandleApp = 'handle.exe' 17 | If(!(gcm $HandleApp)){ 18 | Write-Error "Handle.exe not found at PATH.`nSee https://technet.microsoft.com/en-gb/sysinternals/bb896655.aspx" 19 | break 20 | } 21 | } 22 | Process { 23 | $HandleOut = Invoke-Expression ( $HandleApp +' /accepteula ' + $Filename ) 24 | $Locks = $HandleOut | ? {$_ -match "(.+?)\s+pid: (\d+?)\s+type: File\s+(\w+?): (.+)\s*$"} | % { 25 | [PSCustomObject]@{ 26 | AppName = $Matches[1] 27 | PID = $Matches[2] 28 | FileHandle = $Matches[3] 29 | FilePath = $Matches[4] 30 | } 31 | } 32 | ForEach($Lock in $Locks){ 33 | Invoke-Expression ($HandleApp + " -p " + $Lock.PID + " -c " + $Lock.FileHandle + " -y") | Out-Null 34 | if ( ! $LastexitCode ) { "Successfully closed " + $Lock.AppName + "'s lock on " + $Lock.FilePath} 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MM_FileSystem/Find-UpwardFile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 23-Jan-2016. 3 | Modified: M. Milic 4 | 5 | .SYNOPSIS 6 | Finds the desired file by serarching upwards in the directory hieararchy 7 | .EXAMPLE 8 | PS C:\Windows\System32> Find-UpwardFile explorer.exe 9 | 10 | Returns C:\Windows\explorer.exe 11 | #> 12 | 13 | function Find-UpwardFile { 14 | [CmdletBinding()] 15 | param( 16 | # Name of the file system object which location is searched for 17 | [string]$Name, 18 | # Directory path from which to start searching upward, by default current directory 19 | [string]$StartDir=".", 20 | # Set to return the parent 21 | [switch]$ReturnParent, 22 | # Set to return the file system object instead of string path 23 | [switch]$ReturnObject 24 | ) 25 | 26 | $diStartDir = Get-Item "$StartDir" 27 | if (!$?) { throw "Start directory doesn't exist: $StartDir" } 28 | 29 | while ($diStartDir.GetFileSystemInfos($Name).Length -eq 0) 30 | { 31 | $diStartDir = $diStartDir.Parent 32 | if ($diStartDir -eq $null) {return $null} 33 | } 34 | 35 | $o = $diStartDir.GetFileSystemInfos($Name)[0] 36 | if ($ReturnParent) { 37 | if ($o.Parent -ne $null) { 38 | if ($ReturnObject) { $o.Parent } else {$o.Parent.FullName } 39 | } 40 | else { 41 | if ($ReturnObject) { $o.Parent } else {$o.Directory.FullName} 42 | } 43 | } else { 44 | if ($ReturnObject) {$o} else {$o.FullName} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /MM_Admin/Add-Path.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 29-Nov-2016. 3 | Author: M. Milic 4 | 5 | .SYNOPSIS 6 | Add given directory to the machine PATH environment variable in an idempotent way. 7 | 8 | .PARAMETER path 9 | Absolute or relative directory path. If ommited, defaults to $pwd. 10 | 11 | #> 12 | function Add-Path($path=$pwd, [switch]$Prepend, [switch]$User) 13 | { 14 | 15 | $type = if ($User) { 'User' } else { 'Machine' } 16 | 17 | $path=(gi $path).FullName 18 | if (!(Test-Path $path)) { Write-Error "Path doesn't exist: $path"; return; } 19 | $Env:Path = [System.Environment]::GetEnvironmentVariable("PATH", $type) 20 | 21 | if (!$Env:path.EndsWith(";")) {$Env:Path += ";"} 22 | if ($Env:Path -like "*$path*") {return} 23 | 24 | if ($Prepend) { $Env:Path = $path + $Env:Path } 25 | else { $Env:Path += $path } 26 | 27 | [System.Environment]::SetEnvironmentVariable("PATH", $Env:Path, $type) 28 | 29 | # Notify system of change via WM_SETTINGCHANGE 30 | if (! ("Win32.NativeMethods" -as [Type])) 31 | { 32 | Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" 33 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 34 | public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); 35 | "@ 36 | } 37 | 38 | $HWND_BROADCAST = [IntPtr] 0xffff; $WM_SETTINGCHANGE = 0x1a; $result = [UIntPtr]::Zero 39 | [Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 5000, [ref] $result) | out-null 40 | } 41 | -------------------------------------------------------------------------------- /MM_Admin/Set-EnvironmentVariable.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Mar-2015. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Set enviornment variable 9 | .EXAMPLE 10 | Set-EnvironmentVarable TMP "c:\Windows\TEMP" -Machine -Verbose 11 | 12 | Adds environment varible to the current console and registers it 13 | at the 'machine' level 14 | .EXAMPLE 15 | Set-EnvironmentVarable PATH ";c:\Windows\TEMP" -Verbose -Append 16 | 17 | Appends value to the PATH variable 18 | #> 19 | function Set-EnvironmentVariable() 20 | { 21 | [CmdletBinding(DefaultParameterSetName='Manual')] 22 | param( 23 | # Name of the environment variable, it doesn't have to exist 24 | [string]$Name, 25 | # Value of the environment variable 26 | [string]$Value, 27 | # Set to create environment variable for the machine instead for the current user 28 | [switch]$Machine, 29 | # Set to append Value to the existing value of the environment variable 30 | [switch]$Append, 31 | # Set to prepend a Value to the existing one of the environment variable 32 | [switch]$Prepend 33 | ) 34 | $type = "User" 35 | if ($Machine) { $type = "Machine" } 36 | $old = [System.Environment]::GetEnvironmentVariable($Name, $type) 37 | 38 | if ($Append) {$Value = "${old}${Value}" } 39 | if ($Prepend) {$Value = "${Value}${old}" } 40 | 41 | Set-Item Env:$name $value 42 | [System.Environment]::SetEnvironmentVariable($Name, $Value, $type) 43 | if ($old -ne $null) { 44 | Write-Verbose "$type environment variable changed." 45 | Write-Verbose "Previous value: $old" 46 | } 47 | else { 48 | Write-Verbose "$type environment variable created." 49 | } 50 | } 51 | 52 | Set-Alias env Set-EnvironmentVariable 53 | -------------------------------------------------------------------------------- /MM_Admin/Register-Application.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Register application in the system 4 | 5 | .DESCRIPTION 6 | The function will register application in the system using App Paths registry key so that you 7 | can start it by typing its registred name in the Windows Start menu on using run dialog (Win + R). 8 | 9 | To make applications available in the shell, add to your profile Set-AppKeyAliases function. 10 | 11 | .EXAMPLE 12 | Register-Application 'c:\windows\notepad.exe' 13 | 14 | Register application using name derived from its file name. 15 | 16 | .EXAMPLE 17 | Register-Application 'c:\windows\notepad.exe' -Name ntp 18 | 19 | Register application using explicit name. 20 | 21 | .LINK 22 | Set-AppKeyAliases - https://github.com/majkinetor/posh/blob/master/MM_Admin/Set-AppKeyAliases.ps1 23 | Application Registration - https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx 24 | 25 | #> 26 | function Register-Application{ 27 | [CmdletBinding()] 28 | param( 29 | # Full path of the executable to register. 30 | [Parameter(Mandatory=$true)] 31 | [string]$ExePath, 32 | 33 | # Optional name to register with. By default exe name will be used. 34 | [string]$Name, 35 | 36 | # Register application only for the current user. By default registration is for the machine. 37 | [switch]$User 38 | ) 39 | 40 | if (!(Test-Path $ExePath)) { throw "Path doesn't exist: $ExePath" } 41 | if (!$Name) { $Name = Split-Path $ExePath -Leaf } else { $Name = $Name + '.exe' } 42 | 43 | $appPathKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\$Name" 44 | if ($User) { $appPathKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\$Name" } 45 | 46 | If (!(Test-Path $AppPathKey)) { New-Item "$AppPathKey" | Out-Null } 47 | Set-ItemProperty -Path $AppPathKey -Name "(Default)" -Value $ExePath 48 | } 49 | -------------------------------------------------------------------------------- /MM_Sugar/Test-Credential.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Takes a PSCredential object and validates it against the domain (or local machine, or ADAM instance). 4 | 5 | .LINK 6 | https://serverfault.com/a/286003/23290 7 | #> 8 | function Test-Credential { 9 | param( 10 | # A PSCredential object with the username/password to test. Typically this is generated using the Get-Credential cmdlet. 11 | [parameter(Mandatory=$true, ValueFromPipeline=$true)] 12 | [PSCredential] $Credential, 13 | 14 | # An optional parameter specifying what type of credential this is. Possible values are 'Domain','Machine',and 'ApplicationDirectory.' The default is 'Domain'. 15 | [validateset('Domain','Machine','ApplicationDirectory')] 16 | [string] $Context = 'Domain' 17 | ) 18 | 19 | begin { 20 | Add-Type -AssemblyName System.DirectoryServices.AccountManagement 21 | $DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::$Context) 22 | } 23 | 24 | process { 25 | $DS.ValidateCredentials( $Credential.UserName, $Credential.GetNetworkCredential().Password ) 26 | } 27 | } 28 | 29 | <# 30 | .SYNOPSIS 31 | Takes a PSCredential object and validates it against the domain 32 | 33 | .DESCRIPTION 34 | Sometimes Test-Credential method doesn't work 35 | 36 | .LINK 37 | https://serverfault.com/a/286003/23290 38 | #> 39 | 40 | function Test-ADCredential { 41 | param( 42 | # A PSCredential object with the username/password to test. Typically this is generated using the Get-Credential cmdlet. 43 | [parameter(Mandatory=$true, ValueFromPipeline=$true)] 44 | [PSCredential] $Credential 45 | ) 46 | 47 | $currentDomain = "LDAP://" + ([ADSI]"").distinguishedName 48 | $domain = New-Object System.DirectoryServices.DirectoryEntry($currentDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password) 49 | $null -ne $domain.name 50 | } -------------------------------------------------------------------------------- /MM_Admin/Set-AutoLogon.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 23-Jun-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Set Windows to automatically login as given user after restart and eventually execute a script 7 | 8 | .DESCRIPTION 9 | Enable AutoLogon next time when the server reboots. 10 | It can trigger a specific Script to execute after the server is back online after Auto Logon. 11 | 12 | .EXAMPLE 13 | Set-AutoLogon -Username "domain\user" -Password "my password" 14 | 15 | .EXAMPLE 16 | Set-AutoLogon -Username "domain\user" -Password "my password" -LogonCount 3 17 | 18 | .EXAMPLE 19 | Set-AutoLogon -Username "domain\user" -Password "my password" -Script "c:\test.bat" 20 | #> 21 | 22 | function Set-AutoLogon { 23 | [CmdletBinding()] 24 | Param( 25 | #Provide the username that the system would use to login. 26 | [Parameter(Mandatory=$true)] 27 | [String]$Username, 28 | 29 | #Provide the Password for the User provided. 30 | [Parameter(Mandatory=$true)] 31 | [String]$Password, 32 | 33 | #Sets the number of times the system would reboot without asking for credentials, by default 100000. 34 | [String]$LogonCount=100000, 35 | 36 | #Script: Provide Full path of the script for execution after server reboot 37 | [String]$Script 38 | ) 39 | 40 | $ErrorActionPreference = 'Stop' 41 | 42 | $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" 43 | $RegROPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" 44 | 45 | Set-ItemProperty $RegPath "AutoAdminLogon" -Value 1 46 | Set-ItemProperty $RegPath "DefaultUsername" -Value $Username 47 | Set-ItemProperty $RegPath "DefaultPassword" -Value $Password 48 | #Set-ItemProperty $RegPath "DefaultDomain" -Value $Env:USERDOMAIN 49 | 50 | $v = if ($LogonCount) { $LogonCount } else { '' } 51 | Set-ItemProperty $RegPath "AutoLogonCount" -Value $v -Type DWord 52 | 53 | $v = if ($Script) { $Script } else { '' } 54 | Set-ItemProperty $RegROPath "Set-Autologon" -Value $v 55 | } 56 | -------------------------------------------------------------------------------- /MM_SVN/SVN-OnChange.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 24-Mar-2015. 3 | 4 | <# 5 | .SYNOPSIS 6 | Check SVN path for changes since last run of the command and do actions if needed. 7 | 8 | .DESCRIPTION 9 | Check SVN path for changes and do actions if needed. Actions are scriptblocks which take one 10 | parameter - revision of the change. The function keeps the repository status in the file 'status'. 11 | 12 | .EXAMPLE 13 | SVN-OnChange @{ trunk = { Trunk changed, revision $args[0] }; 'branches/next'= {Next changed} } 14 | 15 | Check 'trunk' and 'branches/next' for changes since last run and display message when it happens. 16 | #> 17 | 18 | function SVN-OnChange { 19 | [CmdletBinding()] 20 | param( 21 | # Hashtable containing repository paths to check (keys) and actions to take (values) 22 | [Parameter(Mandatory=$true, ValueFromPipeLine=$true)] 23 | [ValidateNotNullOrEmpty()] 24 | [HashTable]$Actions 25 | ) 26 | 27 | $ErrorActionPreference = 'stop' 28 | 29 | function get-revision() { 30 | $rev = svn info 2>&1 | sls '^Last Changed Rev:' | out-string 31 | $rev = $rev -split ':' 32 | $rev[1].Trim() 33 | } 34 | 35 | function if-changed([string]$SVNPath, [Scriptblock]$Script) { 36 | Write-Verbose "Checking for changes: $SVNPath" 37 | pushd $SVNPath 38 | $r = get-revision 39 | 40 | if ($r -gt $status[$SVNPath]) { 41 | Write-Verbose "|- '$SVNPath' changed, revision $r" 42 | try { & $Script $r } catch { popd; throw } 43 | } 44 | else { Write-Verbose "|- '$SVNPath' is up to date at revision $r" } 45 | $status[$SVNPath] = $r 46 | popd 47 | } 48 | 49 | 50 | Write-Verbose "Updating repository" 51 | svn update | out-null 52 | 53 | Write-Verbose "Reading status file" 54 | if (Test-Path status) { $status = Import-Clixml status } else { Write-Verbose "|- No status found, creating one."; $status=@{} } 55 | 56 | $Actions.Keys | % { if-changed $_ $Actions[$_] } 57 | 58 | $status | Export-CliXML status 59 | Write-Verbose "Done" 60 | } 61 | -------------------------------------------------------------------------------- /MM_HashTables/ConvertTo-HashTable.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 2.0 2 | <# 3 | .Synopsis 4 | Convert an object into a hashtable. 5 | .Description 6 | This command will take an object and create a hashtable based on its properties. 7 | You can have the hashtable exclude some properties as well as properties that 8 | have no value. 9 | .Notes 10 | Author: Miodrag Milic 11 | Last Change: 27-Mar-2015. 12 | .Example 13 | get-process -id $pid | select name,id,handles,workingset | ConvertTo-HashTable 14 | 15 | Name Value 16 | ---- ----- 17 | WorkingSet 418377728 18 | Name powershell_ise 19 | Id 3456 20 | Handles 958 21 | .Link 22 | About_Hash_Tables 23 | .Inputs 24 | Object 25 | .Outputs 26 | HashTable 27 | #> 28 | 29 | function ConvertTo-HashTable { 30 | [CmdletBinding()] 31 | Param( 32 | [Parameter(Position=0,Mandatory=$True, HelpMessage="Please specify an object",ValueFromPipeline=$True)] 33 | [ValidateNotNullorEmpty()] 34 | # A PowerShell object to convert to a hashtable. If hashtable is passed, simply return it. 35 | [object]$InputObject, 36 | # Do not include object properties that have no value. 37 | [switch]$NoEmpty, 38 | # An array of property names to exclude from the hashtable. 39 | [string[]]$Exclude 40 | ) 41 | 42 | Process { 43 | 44 | $TypeName = [system.type]::GetTypeArray($InputObject).name # Deserialized objects won't have a GetType() method 45 | if ($TypeName -eq [HashTable]) { return $InputObject } 46 | 47 | Write-Verbose "Converting an object of type $TypeName" 48 | 49 | $names = $InputObject | gm -MemberType properties | select -expand name 50 | $hash = @{} 51 | $names | % { 52 | if ($Exclude -contains $_) { return } 53 | if ($NoEmpty -and -not ($inputobject.$_)) { Write-Verbose "Skipping empty $_ "; return } 54 | 55 | Write-Verbose "Adding property $_" 56 | $hash.Add($_,$inputobject.$_) 57 | } 58 | $hash 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /MM_Sugar/Get-Choice.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 17-Jun-2016. 3 | 4 | #requires -version 3 5 | 6 | <# 7 | .SYNOPSIS 8 | Easily create choice 9 | 10 | .EXAMPLE 11 | Get-Choice 12 | 13 | Create default choice with 'yes', 'no', and 'cancel' options and default message 'Are you sure?'. 14 | 15 | .EXAMPLE 16 | choice -Message 'Do you want to keep the files?' -Choices "&Keep", "&Delete", "St&op", "&Skip" 17 | 18 | Create a choice with custom message and 4 custom options 19 | 20 | .EXAMPLE 21 | 22 | choice -Message 'Do you want to keep the files?' -Choices ([ordered]@{"&Keep" = "Keep the files on the disk"; "&Delete" = "Delete the files from the disk"}) 23 | 24 | Create a choice with help messages 25 | .OUTPUT 26 | [String] 27 | #> 28 | function Get-Choice { 29 | [CmdletBinding()] 30 | param( 31 | #Choice title 32 | [string]$Title, 33 | #Choice message 34 | [string]$Message = 'Are you sure?', 35 | #Choice options, array or [ordered]hashable 36 | $Choices = @("&Yes", "&No", "&Cancel"), 37 | #Index of the default option 38 | [int]$Default=0 39 | ) 40 | 41 | if (@([System.Collections.Specialized.OrderedDictionary],[object[]]) -notcontains $Choices.GetType()) { throw "Invalid parameter type for Choices - must be ordered [HashTable] or [Object[]]" } 42 | if ($Choices.Length -eq 0) { throw "Invalid parameter Choices - empty" } 43 | 44 | if ($Choices.GetType() -eq [object[]]) { 45 | $Choices = $Choices | ? {$_.Trim()} #remove invalid options 46 | $Choices | % {$c=@()} {$c += New-Object System.Management.Automation.Host.ChoiceDescription $_, $_.Replace('&','') } 47 | } else { 48 | $Choices = $Choices.GetEnumerator() | ? {$_.Name.Trim()} #remove invalid options 49 | $Choices.GetEnumerator() | % {$c=@()} {$c += New-Object System.Management.Automation.Host.ChoiceDescription $_.Name, $_.Value } 50 | $Choices = $Choices.key 51 | } 52 | 53 | $options = [System.Management.Automation.Host.ChoiceDescription[]]$c 54 | $res = $host.ui.PromptForChoice($Title, $Message, $options, $Default) 55 | 56 | $Choices -split '\n'| select -Index $res | % { $_.Replace('&','') } 57 | } 58 | 59 | sal choice Get-Choice 60 | -------------------------------------------------------------------------------- /MM_FileSystem/Start-FileSystemWatcher.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Last Change: 14-Feb-2016. 3 | Modified: M. Milic 4 | 5 | .SYNOPSIS 6 | Monitor directory for changes and call user action 7 | .NOTE 8 | # Adapted from: https://mcpmag.com/articles/2015/09/24/changes-to-a-folder-using-powershell.aspx 9 | #> 10 | function Start-FileSystemWatcher { 11 | [cmdletbinding()] 12 | Param ( 13 | [string]$Path=$pwd, 14 | [ValidateSet('Changed','Created','Deleted','Renamed')] 15 | [string[]]$EventName= @('Changed','Created','Deleted','Renamed'), 16 | [string]$Filter, 17 | [System.IO.NotifyFilters]$NotifyFilter="FileName, DirectoryName, Attributes, Size, LastWrite, LastAccess, CreationTime, Security", 18 | [switch]$Recurse, 19 | [scriptblock]$Action, 20 | [int]$Throttle=-1 21 | ) 22 | 23 | $FileSystemWatcher = New-Object System.IO.FileSystemWatcher 24 | $FileSystemWatcher.Path = $Path 25 | $FileSystemWatcher.Filter = $Filter 26 | $FileSystemWatcher.NotifyFilter = $NotifyFilter 27 | $FileSystemWatcher.IncludeSubdirectories = $Recurse 28 | 29 | $FullAction= { 30 | switch ($Event.SourceEventArgs.ChangeType) { 31 | 'Renamed' { 32 | $msg = "{0} was {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath, $Event.SourceEventArgs.ChangeType, 33 | $Event.SourceArgs[-1].FullPath, $Event.SourceEventArgs.TimeGenerated 34 | } 35 | default { $msg = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath, $Event.SourceEventArgs.ChangeType, $Event.TimeGenerated } 36 | } 37 | $whcolors = @{ ForegroundColor = 'Green'; BackgroundColor = 'Black' } 38 | Write-Host @whcolors $msg 39 | Write-Host @whcolors "Executing user action" 40 | $res = iex $event.MessageData 41 | Write-Host @whcolors "User action result: $res" 42 | } 43 | 44 | $ObjectEventParams = @{ InputObject = $FileSystemWatcher; Action = $FullAction; MessageData = $Action } 45 | forEach ($Item in $EventName) { 46 | $ObjectEventParams.EventName = $Item 47 | $ObjectEventParams.SourceIdentifier = "File.$($Item)" 48 | Write-Verbose "Starting watcher for Event: $($Item)" 49 | Register-ObjectEvent @ObjectEventParams | out-null 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MM_HashTables/Merge-Hashtables.ps1: -------------------------------------------------------------------------------- 1 | #requires -Version 2.0 2 | <# 3 | .SYNOPSIS 4 | Create new HashTable from two HashTables where the second given 5 | HashTable will override. 6 | 7 | .DESCRIPTION 8 | In case of duplicate keys the second HashTable's key values "win". 9 | Nested and ordered HashTables are supported. 10 | 11 | .EXAMPLE 12 | $configData = Merge-Hashtables -First $defaultData -Second $overrideData 13 | #> 14 | function Merge-Hashtables 15 | { 16 | [CmdletBinding()] 17 | param ( 18 | # Base HashTable 19 | $First, 20 | 21 | # Overriding HashTable 22 | $Second 23 | ) 24 | 25 | function set_keys ($First, $Second) 26 | { 27 | @($First.Keys) | ? { $Second.Keys -contains $_ } | % { 28 | if ( is_HashTable $First.$_ $Second.$_ ) { set_keys $First.$_ $Second.$_ } 29 | else { 30 | $First.$_ = $Second.$_ 31 | } 32 | } 33 | } 34 | 35 | function add_keys ($First, $Second) 36 | { 37 | @($Second.Keys) | % { 38 | if ($First.Keys -contains $_) { 39 | if (is_HashTable $Second.$_ $First.$_) { add_keys $First.$_ $Second.$_ } 40 | } else { $First.$_ = $Second.$_ } 41 | } 42 | } 43 | 44 | function clone( $DeepCopyObject ) 45 | { 46 | if (!$DeepCopyObject) { return $DeepCopyObject } 47 | $memStream = new-object IO.MemoryStream 48 | $formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter 49 | $formatter.Serialize($memStream,$DeepCopyObject) 50 | $memStream.Position=0 51 | $formatter.Deserialize($memStream) 52 | } 53 | 54 | function is_HashTable() { 55 | foreach ($h in $args) { 56 | $b = ($h -is [HashTable]) -or ($h -is [System.Collections.Specialized.OrderedDictionary]) 57 | if (!$b) { return $false } 58 | } 59 | return $true 60 | } 61 | 62 | if (!$Second) { return (clone $First) } 63 | if (!$First) { return (clone $Second) } 64 | 65 | $firstClone = clone $First 66 | $secondClone = clone $Second 67 | 68 | set_keys $firstClone $secondClone 69 | add_keys $firstClone $secondClone 70 | 71 | $firstClone 72 | } 73 | 74 | # $a = [ordered]@{ 75 | # x = 1 76 | # y = 2 77 | # z = [ordered]@{ 78 | # z1=1 79 | # z2=2 80 | # } 81 | # } 82 | 83 | # $b = [ordered] @{ 84 | # x = 'a' 85 | # z = [ordered]@{ z2='a'} 86 | # k = 'a' 87 | # } 88 | 89 | 90 | # $c = Merge-Hashtables $a $b 91 | # $c 92 | -------------------------------------------------------------------------------- /MM_HashTables/Write-HashTable.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Write nested HashTable to output 4 | 5 | .DESCRIPTION 6 | The usual HashTable output function do not display nested hashtables. 7 | #> 8 | function Write-HashTable { 9 | param( 10 | # ([ordered]) Hashtable to display 11 | [Parameter(ValueFromPipeline=$true)] 12 | $HashTable, 13 | 14 | # List of keys to include (only first level) 15 | [string[]] $Include, 16 | 17 | # List of keys to exclude from all nested HashTables 18 | [string[]] $Exclude, 19 | 20 | # List of keys to hide - values are replaced with '*****' 21 | [string[]] $Hide, 22 | 23 | # One line output 24 | [switch] $OneLine, 25 | 26 | [Parameter(DontShow = $true)] 27 | [int] $Indent 28 | ) 29 | 30 | $HashTable.Keys | % { $max_len=0 } { $l = $_.ToString().Length; if ($l -gt $max_len) { $max_len = $l } } 31 | $res = '' 32 | foreach ($kv in $HashTable.GetEnumerator()) { 33 | if ($res) { if (!$OneLine) { $res += "`n" } else { $res += " | "} } 34 | if ($kv.Key -in $Exclude) { continue } 35 | if ($Include -and ($kv.Key -notin $Include)) { continue } 36 | 37 | $is_HashTable = ($kv.Value -is [HashTable]) -or ($kv.Value -is [System.Collections.Specialized.OrderedDictionary]) 38 | if ($is_HashTable) { 39 | $val = Write-HashTable $kv.Value -Hide $Hide -Include $null -Exclude $Exclude -Indent ($Indent + 2) -OneLine:$OneLine 40 | $val = $val.Substring(1) 41 | } 42 | elseif ($kv.Value -is [Array] ) { 43 | $v = $kv.Value[0..3] -join ', ' 44 | $v += if ($kv.Value.Count -gt 5) { '...' } 45 | $val = "{" + $v + "}" 46 | } 47 | else { 48 | $val = if ($kv.Value) { $kv.Value.ToString() } else { '' } 49 | if ($val.IndexOf("`n") -ne -1) { 50 | $val = $val -split "`n" 51 | $val = $val[0] + ($val[1..5] | % { "`n" + ' '*($max_len+3+$Indent) + $_}) 52 | } 53 | } 54 | 55 | if ($kv.Key -in $Hide) { 56 | $rval = '*****' 57 | $val = if ($is_HashTable) { $val -replace '(?<=: ).+', $rval } else { $rval } 58 | } 59 | 60 | $key = $kv.Key.ToString() 61 | if (!$OneLine) { $key = ' '*$Indent + $key.PadRight($max_len) + ' ' } 62 | 63 | $res += if ($is_HashTable) { $key; "`n"; $val } else { $key + ': ' + $val } 64 | } 65 | $res 66 | } 67 | 68 | # $x = [ordered]@{ 69 | # x=1 70 | # y=2 71 | # z=@{ x=1; p = 1} 72 | # } 73 | 74 | # Write-HashTable $x -Include 'z' -------------------------------------------------------------------------------- /MM_FileSystem/Get-FileEncoding.ps1: -------------------------------------------------------------------------------- 1 | function Get-FileEncoding { 2 | ## Get-FileEncoding http://poshcode.org/2153 3 | <# 4 | 5 | .SYNOPSIS 6 | 7 | Gets the encoding of a file 8 | 9 | .EXAMPLE 10 | 11 | Get-FileEncoding.ps1 .\UnicodeScript.ps1 12 | 13 | BodyName : unicodeFFFE 14 | EncodingName : Unicode (Big-Endian) 15 | HeaderName : unicodeFFFE 16 | WebName : unicodeFFFE 17 | WindowsCodePage : 1200 18 | IsBrowserDisplay : False 19 | IsBrowserSave : False 20 | IsMailNewsDisplay : False 21 | IsMailNewsSave : False 22 | IsSingleByte : False 23 | EncoderFallback : System.Text.EncoderReplacementFallback 24 | DecoderFallback : System.Text.DecoderReplacementFallback 25 | IsReadOnly : True 26 | CodePage : 1201 27 | 28 | #> 29 | 30 | param( 31 | ## The path of the file to get the encoding of. 32 | $Path 33 | ) 34 | 35 | Set-StrictMode -Version Latest 36 | 37 | ## The hashtable used to store our mapping of encoding bytes to their 38 | ## name. For example, "255-254 = Unicode" 39 | $encodings = @{} 40 | 41 | ## Find all of the encodings understood by the .NET Framework. For each, 42 | ## determine the bytes at the start of the file (the preamble) that the .NET 43 | ## Framework uses to identify that encoding. 44 | $encodingMembers = [System.Text.Encoding] | Get-Member -Static -MemberType Property 45 | 46 | $encodingMembers | Foreach-Object { 47 | $encodingBytes = [System.Text.Encoding]::($_.Name).GetPreamble() -join '-' 48 | $encodings[$encodingBytes] = $_.Name 49 | } 50 | 51 | ## Find out the lengths of all of the preambles. 52 | $encodingLengths = $encodings.Keys | Where-Object { $_ } | 53 | Foreach-Object { ($_ -split '-').Count } 54 | 55 | ## Assume the encoding is UTF7 by default 56 | $result = 'UTF7' 57 | 58 | ## Go through each of the possible preamble lengths, read that many 59 | ## bytes from the file, and then see if it matches one of the encodings 60 | ## we know about. 61 | foreach($encodingLength in $encodingLengths | Sort-Object -Descending) 62 | { 63 | $bytes = (Get-Content -encoding byte -readcount $encodingLength $path)[0] 64 | $encoding = $encodings[$bytes -join '-'] 65 | 66 | ## If we found an encoding that had the same preamble bytes, 67 | ## save that output and break. 68 | if($encoding) 69 | { 70 | $result = $encoding 71 | break 72 | } 73 | } 74 | 75 | ## Finally, output the encoding. 76 | [System.Text.Encoding]::$result 77 | } -------------------------------------------------------------------------------- /MM_HashTables/Edit-HashTable.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Transform given hashtable via deep iterator to new hashtable 4 | 5 | .DESCRIPTION 6 | Provide a scriptblock that processes each element of an existing hashtable. Change, add or remove elements by 7 | returning array of key/value pairs. 8 | 9 | .PARAMETER Hash 10 | ([ordered]) Hashtable to be transformed 11 | 12 | .PARAMETER Action 13 | ScriptBlock to call on each element. Accepts arguments $key, $value, $parent. Parent is array of parent based on hierarchy. 14 | Returns array (key, val, key1, val1, .... keyN, valN). 15 | First 2 will be used instead of the original key/val pairs. Pass $null as key to delete the key/value pair in the resulting table. 16 | Other keys (1..N) will be addeded to resulting table. 17 | 18 | If value is an [Array] and $EnumerateArrays switch is present, the Action will get called with the $key parameter set to $null and the $value parameter set to array value. 19 | Return $false to remove array item from resulting array or ($true, $item1 ... $itemN) to replace the current $value with $items[1..N]. 20 | 21 | .EXAMPLE 22 | Edit-HashTable @{x=1; y=2; z=@{p=3}} { param( $key, $value ) $key.ToUpper(), $value } 23 | 24 | Transform keys to uppercase 25 | 26 | .EXAMPLE 27 | Edit-HashTable ([ordered]@{x=1; y=2; z=1,2,3,4}) { param( $key, $value ) 28 | if (!$key) { return $true, $value, ($value*10) } 29 | else { 30 | if ($value -isnot [Array]) { $key, $value, "$key*10", ($value*10) } else { $key, $value } 31 | } 32 | } 33 | 34 | Transform hashtable to multiply each key with 10 and add it as a new hashtable element. 35 | If array is encountered, each element is multiplied with 10 and added to the array besides original elements. 36 | 37 | #> 38 | function Edit-HashTable ($Hash, [ScriptBlock] $Action, [switch] $EnumerateArrays, [Parameter(DontShow = $true)] [string[]] $Parent ) { 39 | function is_hashtable { ($args[0] -is [HashTable]) -or ($args[0] -is [System.Collections.Specialized.OrderedDictionary]) } 40 | 41 | $res = if ($Hash -is [HashTable]) { @{} } else { [ordered]@{} } 42 | 43 | $Hash.Keys | % { 44 | $key = $_ 45 | $val = $Hash.$key 46 | 47 | if ($val -ne $null) { 48 | if (is_hashtable $val) { 49 | $val = Edit-HashTable $val $Action ($Parent + $key) 50 | } 51 | 52 | if (($val -is [Array]) -and $EnumerateArrays) { 53 | $a=@() 54 | foreach ($item in $val) { 55 | $b = & $Action $null $item "$($Parent).$($key)" 56 | if ( !$b[0] ) { continue } 57 | $a += $b[1..($b.Count)] 58 | } 59 | $val = $a 60 | } 61 | } 62 | $a = & $Action $key $val $parent 63 | for ($i=0; $i -lt $a.Count; $i+=2) { 64 | $k = $a[$i]; $v = $a[$i+1] 65 | if ($k) { $res.$k = $v } 66 | } 67 | } 68 | $res 69 | } -------------------------------------------------------------------------------- /MM_Network/Get-WirelessNetwork.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 16-Nov-2016. 3 | 4 | #requires -version 3 5 | 6 | <# 7 | .SYNOPSIS 8 | List wireless networks 9 | 10 | .DESCRIPTION 11 | Uses netsh wlan to get wireless network information in Powershell friendly way. 12 | 13 | .EXAMPLE 14 | Get-WirelessNetwork 15 | 16 | Get all visible wireless networks. 17 | 18 | .EXAMPLE 19 | Get-WirelessNetwork android 20 | 21 | Get only wireless networks that have android in its name. 22 | 23 | .EXAMPLE 24 | Get-WirelessNetwork | ? Authentication -eq 'open' 25 | 26 | Get only wireless networks with open authentication. 27 | 28 | .EXAMPLE 29 | Get-WirelessNetwork | ? { [int]$_.Signal -gt 70 } 30 | 31 | Get only wireless networks with signal stronger then 70% 32 | #> 33 | function Get-WirelessNetwork() { 34 | [CmdletBinding()] 35 | param( 36 | # List of Regex criteria for SSID. Ommit to get everything. 37 | [string[]]$SSID 38 | ) 39 | Write-Verbose $PSCmdlet.MyInvocation.PipelinePosition 40 | 41 | if (([int](gwmi win32_operatingsystem).Version.Split(".")[0]) -lt 6) { throw "Requires Windows Vista or higher." } 42 | if ((gsv "wlansvc").Status -ne "Running" ) { throw "Wlan service is not running." } 43 | 44 | $ifaces = netsh wlan show interfaces 45 | $ifaces_names = $ifaces | sls '^\s*Name\s*:\s*(.+)\s*' | % { $_.matches[0].Groups[1].Value } 46 | 47 | $props = @( 'SSID', 'Authentication', 'Encryption', 'Signal', 'Radiotype', 'Channel', 'BSSID', 'Interface', 'Status' ) 48 | $nt = "" | select -Property $props 49 | $n = $nt.PSObject.Copy() 50 | $results = @() 51 | $ifaces_names | % { 52 | $iface = $_ 53 | while(1) { 54 | $netsh = netsh wlan show network mode=bssid interface="$iface" #repeat command until we get all the data 55 | if ($netsh -match 'Signal') { 56 | $iface_profiles = netsh wlan show profile interface="$iface" 57 | break 58 | } 59 | sleep -Milliseconds 300 60 | } 61 | 62 | $netsh | select -Skip 4 | % { 63 | if (!$_) { 64 | $n.Interface = $iface 65 | $n.Signal = $n.Signal -replace '.$' 66 | 67 | $status = $ifaces | sls "^\s*SSID\s*:\s*$($n.SSID)\s*" -Context 1,0 68 | $status = $status -split "\n|:" | select -Index 1 | % Trim 69 | if ($status -eq 'connected') { $n.Status = "Connected" } 70 | elseif ($iface_profiles -match $n.SSID) { $n.Status = "Disconnected" } 71 | 72 | $results += $n; $n = $nt.PSObject.Copy() 73 | return 74 | } 75 | $a = $_ -split ' : ' 76 | $p = $a[0].Trim() -replace '[ \d]*'; $v = $a[1].Trim() 77 | if ($props -contains $p) { $n.$p = $v } 78 | } 79 | } 80 | $r = $results 81 | if ($SSID) { 82 | $SSID | % {$re=""}{ $re += "({0})|" -f $_ }; $re = $re -replace '.$' 83 | $r = $results | ? SSID -match $re 84 | } 85 | $r = $r | sort {[int]($_.Signal -replace "%")} -Descending 86 | $r 87 | } 88 | -------------------------------------------------------------------------------- /MM_Data/Ini.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Author: M. Milic 3 | 4 | Quick and dirty Ini functions 5 | #> 6 | 7 | <# 8 | .SYNOPSIS 9 | Convert ini string to ordered HashTable 10 | #> 11 | function ConvertFrom-Ini{ 12 | param( 13 | # Array of strings representing ini content 14 | [Parameter(ValueFromPipeline=$true)] 15 | [string[]] $InputObject, 16 | 17 | # Trim key and section values 18 | [switch] $Trim 19 | ) 20 | 21 | $ini = [ordered]@{} 22 | switch -Regex ($InputObject -split "`r`n" ) { 23 | "^\s*\[(.+)\]\s*$" { 24 | $section = $matches[1]; if ($Trim) { $section = $section.Trim() } 25 | $ini.$section = [ordered]@{} 26 | } 27 | "^\s*(.+?)\s*=(.*)" { 28 | $key, $value = $matches[1..2]; if ($Trim) { $value = $value.Trim() } 29 | if ($key.StartsWith(";")) { continue } # skip comments 30 | $ini.$section.$key = $value; 31 | } 32 | } 33 | $ini 34 | } 35 | 36 | <# 37 | .SYNOPSIS 38 | Set ini value 39 | 40 | .DESCRIPTION 41 | Set ini value in memory using regular expression replace. 42 | This is not fast, but ini files are usually very small and this keeps original formating, comments etc. 43 | #> 44 | function Set-IniValue { 45 | param( 46 | # Ini string 47 | [Parameter(ValueFromPipeline=$true)] 48 | [string] $InputObject, 49 | 50 | [Parameter(Mandatory=$true)] 51 | [string] $Section, 52 | 53 | [Parameter(Mandatory=$true)] 54 | [string] $Key, 55 | 56 | # Key value; if null, key will be deleted 57 | $Value = $null 58 | ) 59 | 60 | $remove = $Value -eq $null 61 | $line = if ($remove) {''} else { "$Key=$Value" } 62 | 63 | $sectionRe = '(?<=(?:\s*\n)+)\[\s*(?
.+?)\s*\]\s*\n(?(?:.|\n)*?)(?=(?:\s*\n)*\[)' 64 | $m = "`n$ini`n[" | sls -AllMatches $sectionRe 65 | $matchSection = $m.Matches | ? { $_.Groups['Section'].Value -eq $Section } 66 | if (!$matchSection) { return $( if ($remove) {} else { "$ini`n[$Section]`n$line" } ) } 67 | 68 | $matchKeys = $matchSection.Groups['Keys'] 69 | $keys = $matchKeys.Value 70 | if ($keys -and ($m = "`n$keys`n" | sls "\s*\n$Key\s*=.+(?=\n)")) { 71 | $idxStart = $matchKeys.Index + $m.Matches[0].Index - 2 72 | $idxEnd = $idxStart + $m.Matches[0].Length 73 | if (!$remove) { $line = "`n$line"} 74 | $ini = $ini.Substring(0, $idxStart) + $line + $ini.Substring($idxEnd) 75 | } else { 76 | if ($remove) { return } 77 | $ini = $ini -replace "`n\s*\[\s*$Section\s*\]\s*", "`$0`n$line`n" 78 | } 79 | $ini 80 | } 81 | 82 | $ini = @" 83 | [S1] 84 | foo = boo 85 | faa=baaa 86 | 87 | [S2] 88 | foo = boo 89 | saa saa = b1 c2 d3 90 | p=l 91 | [Empty] 92 | ;Empty with comment 93 | [Empty2] 94 | 95 | [Empty3] 96 | 97 | [S3] 98 | 99 | ; Some comment here 100 | foo= boo 101 | 102 | ; More comments 103 | sa = ba 104 | 105 | [Meh] 106 | "@ 107 | 108 | # $ini = gc "$Env:AppData\Ghisler\wincmd.ini" -Encoding UTF8 -Raw 109 | #$ini = Set-IniValue $ini FileSystemPlugins Uninstaller64 meh 110 | #$ini = Set-IniValue $ini FileSystemPlugins64 Uninstaller64 1 111 | #$ini | Out-File temp.ini -------------------------------------------------------------------------------- /MM_Desktop/Set-PinnedApplication.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 29-Jan-2016. 3 | # Adapted from: http://goo.gl/xvHcSE 4 | 5 | <# 6 | .SYNOPSIS 7 | This function are used to pin and unpin programs from the taskbar and Start-menu. 8 | 9 | .EXAMPLE 10 | Set-PinnedApplication -Action PinToTaskbar -FilePath "C:\WINDOWS\system32\notepad.exe" 11 | 12 | .EXAMPLE 13 | gcm notepad,explorer | Set-PinnedApplication -Action PinToTaskbar -Verbose 14 | 15 | .NOTES 16 | Tested on platforms: Windows 7, Windows Server 2008 R2, Windows 8.1, Windows 10 17 | #> 18 | function Set-PinnedApplication 19 | { 20 | [CmdletBinding()] 21 | param( 22 | # Action to take: PinToTaskbar (default), PinToStartMenu, UnPinFromTaskbar, UnPinFromStartMenu 23 | [ValidateSet('PinToTaskbar', 'PinToStartMenu', 'UnPinFromTaskbar', 'UnPinFromStartMenu')] 24 | [string]$Action='PinToTaskbar', 25 | 26 | # Path to executable for the action 27 | [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFrompiPelinebyPropertyName=$true)] 28 | [Alias('Path')] 29 | [string[]]$FilePath 30 | ) 31 | 32 | begin 33 | { 34 | function Invoke-Verb ([string]$FilePath, $verb) 35 | { 36 | $verb = $verb.Replace("&","") 37 | $path = split-path $FilePath 38 | $shell = new-object -com "Shell.Application" 39 | $folder = $shell.Namespace($path) 40 | $item = $folder.Parsename((split-path $FilePath -leaf)) 41 | $itemVerb = $item.Verbs() | ? {$_.Name.Replace("&","") -eq $verb} 42 | if($itemVerb -eq $null){ throw "Verb $verb not found." } else { $itemVerb.DoIt() } 43 | } 44 | 45 | function Get-Verb ($verbId) 46 | { 47 | try { 48 | $t = [type]"CosmosKey.Util.MuiHelper" 49 | } catch { 50 | $def = @" 51 | 52 | [DllImport("user32.dll")] 53 | public static extern int LoadString(IntPtr h,uint id, System.Text.StringBuilder sb,int maxBuffer); 54 | 55 | [DllImport("kernel32.dll")] 56 | public static extern IntPtr LoadLibrary(string s); 57 | "@ 58 | Add-Type -MemberDefinition $def -name MuiHelper -namespace CosmosKey.Util 59 | } 60 | if($global:CosmosKey_Utils_MuiHelper_Shell32 -eq $null){ 61 | $global:CosmosKey_Utils_MuiHelper_Shell32 = [CosmosKey.Util.MuiHelper]::LoadLibrary("shell32.dll") 62 | } 63 | 64 | $maxVerbLength = 255 65 | $verbBuilder = new-object Text.StringBuilder "",$maxVerbLength 66 | [void][CosmosKey.Util.MuiHelper]::LoadString($CosmosKey_Utils_MuiHelper_Shell32, $verbId, $verbBuilder, $maxVerbLength) 67 | return $verbBuilder.ToString() 68 | } 69 | 70 | $verbs = @{ 71 | "PintoStartMenu" = 5381 72 | "UnpinfromStartMenu" = 5382 73 | "PintoTaskbar" = 5386 74 | "UnpinfromTaskbar" = 5387 75 | } 76 | } 77 | process { 78 | $FilePath | % { 79 | if (!(Test-Path $_)) {Write-Verbose "Path doesn't exist: $_"; return} 80 | Write-Verbose "$Action for $_" 81 | InvokeVerb -FilePath $_ -Verb $(GetVerb -VerbId $verbs.$action) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /MM_TotalCmd/Ini.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Author: M. Milic 3 | 4 | Quick and dirty Ini functions 5 | #> 6 | 7 | <# 8 | .SYNOPSIS 9 | Convert ini string to ordered HashTable 10 | #> 11 | function ConvertFrom-Ini{ 12 | param( 13 | # Array of strings representing ini content 14 | [Parameter(ValueFromPipeline=$true)] 15 | [string[]] $InputObject, 16 | 17 | # Trim key and section values 18 | [switch] $Trim 19 | ) 20 | 21 | $ini = [ordered]@{} 22 | switch -Regex ($InputObject -split "`r`n" ) { 23 | "^\s*\[(.+)\]\s*$" { 24 | $section = $matches[1]; if ($Trim) { $section = $section.Trim() } 25 | $ini.$section = [ordered]@{} 26 | } 27 | "^\s*(.+?)\s*=(.*)" { 28 | $key, $value = $matches[1..2]; if ($Trim) { $value = $value.Trim() } 29 | if ($key.StartsWith(";")) { continue } # skip comments 30 | $ini.$section.$key = $value; 31 | } 32 | } 33 | $ini 34 | } 35 | 36 | <# 37 | .SYNOPSIS 38 | Set ini value 39 | 40 | .DESCRIPTION 41 | Set ini value in memory using regular expression replace. 42 | This is not fast, but ini files are usually very small and this keeps original formating, comments etc. 43 | #> 44 | function Set-IniValue { 45 | param( 46 | [Parameter(Mandatory=$true)] 47 | [string] $Section, 48 | 49 | [Parameter(Mandatory=$true)] 50 | [string] $Key, 51 | 52 | # Key value; if null, key will be deleted 53 | $Value = $null, 54 | 55 | # Ini string 56 | [Parameter(ValueFromPipeline=$true)] 57 | [string] $InputObject 58 | ) 59 | $ini = $InputObject 60 | 61 | $remove = $Value -eq $null 62 | $line = if ($remove) {''} else { "$Key=$Value" } 63 | 64 | $sectionRe = '(?<=(?:\s*\n)+)\[\s*(?
.+?)\s*\]\s*\n(?(?:.|\n)*?)(?=(?:\s*\n)*\[)' 65 | $m = "`n$ini`n[" | sls -AllMatches $sectionRe 66 | $matchSection = $m.Matches | ? { $_.Groups['Section'].Value -eq $Section } 67 | if (!$matchSection) { return $( if ($remove) {} else { "$ini`n[$Section]`n$line" } ) } 68 | 69 | $matchKeys = $matchSection.Groups['Keys'] 70 | $keys = $matchKeys.Value 71 | if ($keys -and ($m = "`n$keys`n" | sls "\s*\n$Key\s*=.+(?=\n)")) { 72 | $idxStart = $matchKeys.Index + $m.Matches[0].Index - 2 73 | $idxEnd = $idxStart + $m.Matches[0].Length 74 | if (!$remove) { $line = "`n$line"} 75 | $ini = $ini.Substring(0, $idxStart) + $line + $ini.Substring($idxEnd) 76 | } else { 77 | if ($remove) { return } 78 | $ini = $ini -replace "`n\s*\[\s*$Section\s*\]\s*", "`$0`n$line`n" 79 | } 80 | $ini 81 | } 82 | 83 | # $ini = @" 84 | # [S1] 85 | # foo = boo 86 | # faa=baaa 87 | 88 | # [S2] 89 | # foo = boo 90 | # saa saa = b1 c2 d3 91 | # p=l 92 | # [Empty] 93 | # ;Empty with comment 94 | # [Empty2] 95 | 96 | # [Empty3] 97 | 98 | # [S3] 99 | 100 | # ; Some comment here 101 | # foo= boo 102 | 103 | # ; More comments 104 | # sa = ba 105 | 106 | # [Meh] 107 | # "@ 108 | 109 | 110 | # $ini = gc "$Env:AppData\Ghisler\wincmd.ini" -Encoding UTF8 -Raw 111 | #$ini = Set-IniValue $ini FileSystemPlugins Uninstaller64 meh 112 | #$ini = Set-IniValue $ini FileSystemPlugins64 Uninstaller64 1 113 | #$ini | Out-File temp.ini -------------------------------------------------------------------------------- /MM_Admin/Register-LoginTask.ps1: -------------------------------------------------------------------------------- 1 | # Last Change: 16-Aug-2016. 2 | # Author: M. Milic 3 | 4 | #requires -version 4.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Registers login task for the current user using Task Scheduler. 9 | 10 | .DESCRIPTION 11 | This function uses Task Scheduler to register login task or Powershell script 12 | for the current user. This is better then using Startup directory or registry RUN key 13 | because it has more options and behaves better with UAC. 14 | 15 | The function creates the task inside the $Env:USERDOMAIN\$Env:USERNAME\Startup path. 16 | 17 | .EXAMPLE 18 | Register-LoginTask "$Env:PROGRAMFILES\Everything\Everything.exe" -Arguments '-startup' -RunElevated -Delay "00:00:30" 19 | 20 | Starts everything.exe after login in the background in a elevated mode with random 30 second delay. 21 | 22 | .EXAMPLE 23 | Get-ScheduledTask -TaskPath *$Env:USERNAME* | ? TaskName -like '*everything*' | Unregister-ScheduledTask 24 | 25 | Unregister previously created login task. 26 | 27 | .EXAMPLE 28 | '"Hello $($args[0])" > out.txt; ' > test.ps1 29 | Register-LoginTask test.ps1 -Arguments 'Foo Bar' -Verbose 30 | 31 | Register login script with arguments. Show Powershell arguments in verbose output. 32 | 33 | .EXAMPLE 34 | Get-ScheduledTask -TaskPath *$Env:USERNAME* 35 | 36 | List all registered tasks for the user. 37 | 38 | .NOTE 39 | Requires Windows 8+ 40 | #> 41 | function Register-LoginTask() 42 | { 43 | [CmdletBinding()] 44 | param( 45 | # Executable: Path to exe or Powershell script file (*.ps1) 46 | [Parameter(Mandatory=$true, ValueFromPipeline=$True, Position=0)] 47 | [Alias("Path", "FullName")] 48 | [string]$Execute, 49 | # Arguments to the program or script to execute 50 | [string]$Arguments, 51 | # Maximum value of random delay, 0 by default 52 | [timespan] $Delay=(New-Timespan), 53 | # Execution limit, by default indefinite (more precise, 27.7 years) 54 | [timespan] $Limit=(New-TimeSpan -Days 9999), 55 | # Run with highest privilege 56 | [switch]$RunElevated, 57 | # User, by default current one 58 | $User = "$env:USERDOMAIN\$env:USERNAME" 59 | ) 60 | 61 | if (!(Test-Path $Execute)) { throw "Invalid path: $Execute" } 62 | $isScript = (gi $Execute).Extension -eq '.ps1' 63 | 64 | Write-Verbose "User: $user" 65 | 66 | $Execute = Resolve-Path $Execute 67 | $params = @{ Execute = $Execute; WorkingDirectory = Split-Path $Execute } 68 | if (![string]::IsNullOrWhiteSpace($Arguments)) { $params.Argument = $Arguments } 69 | $a = New-ScheduledTaskAction @params 70 | $t = New-ScheduledTaskTrigger -AtLogon -User $user -RandomDelay $Delay 71 | $s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $Limit -DontStopIfGoingOnBatteries -AllowStartIfOnBatteries -Compatibility Win8 -StartWhenAvailable 72 | 73 | $params = @{ 74 | Force = $True 75 | TaskPath = "$user\Startup" 76 | Action = $a 77 | Trigger = $t 78 | Settings = $s 79 | Taskname = (Split-Path -Leaf $Execute) 80 | } 81 | if ($RunElevated) {$params.RunLevel="Highest"} 82 | if ($isScript) { 83 | $params.TaskName = "PS - $(Split-Path -Leaf $Execute)" 84 | $scriptPath = Resolve-Path $Execute 85 | $sa = "-NoProfile -NoLogo -WindowStyle Hidden -NonInteractive -ExecutionPolicy Bypass -Command `"cd `$HOME; . '$scriptPath' $Arguments`"" 86 | Write-Verbose "Registering login script. Powershell arguments:`n$sa" 87 | $params.Action = New-ScheduledTaskAction -Execute "$PSHome\powershell.exe" -Argument $sa 88 | } else { Write-Verbose "Registering login executable: $Execute $Arguments" } 89 | 90 | Register-ScheduledTask @params 91 | } 92 | -------------------------------------------------------------------------------- /MM_VPN/vpn.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 23-Feb-2016. 3 | 4 | #requires -version 3.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Connect to the VPN network 9 | 10 | .EXAMPLE 11 | PS> connect-vpn myvpn\companyXYZ 12 | 13 | Connect to the VPN network using configuration file myvpn\companyXYZ. 14 | 15 | .EXAMPLE 16 | PS> vpnc myvpn\companyXYZ -Timeout 120 17 | 18 | Connect to the VPN network in the background job. Terminate the job after 120s if still running. 19 | #> 20 | function connect-vpn( [string] $ConfigPath, [int] $Timeout = -1 ) { 21 | $log = "$PSScriptRoot\vpn.log" 22 | 23 | "-"*50 | tee $log -Append 24 | "Connect-VPN started at " + [DateTime]::UtcNow.ToString("s").Replace(':','-') | tee $log -Append 25 | 26 | if ($Timeout -eq -1) { connect-vpnfg $ConfigPath | tee $log -Append } 27 | else { connect-vpnbg $ConfigPath $Timeout | tee $log -Append } 28 | } 29 | 30 | function connect-vpnfg( [string]$ConfigPath ) { 31 | $ConfigPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($ConfigPath) 32 | if (!(Test-Path $ConfigPath)) { throw "Unable to find config path: $ConfigPath" } 33 | 34 | $name = Split-Path -Leaf $ConfigPath 35 | "Connecting to VPN network using configuration '$name'" 36 | 37 | ps vpnui -ea 0 | kill | out-null #vpncli doesn't work along with vpnui 38 | disconnect-vpn | out-null 39 | gc $ConfigPath | vpncli -s | compact-log 40 | vpnui 41 | 42 | "Connected to VPN network using configuration '$name'" 43 | } 44 | 45 | function connect-vpnbg( [string] $ConfigPath, [int] $Timeout ) { 46 | $script = [scriptblock]::Create(@" 47 | . $PSScriptRoot\vpn.ps1 48 | connect-vpnfg $ConfigPath 49 | "@) 50 | $job = Start-Job -script $script -name (Split-Path -leaf $ConfigPath) 51 | "Started background vpn connection: $($job.Name)" 52 | wait-job $job -Timeout $Timeout 53 | if ($job.State -eq 'Running') { "Timeout exceeded, terminating job" } 54 | 55 | $out = receive-job $job; remove-job $job -force 56 | $out 57 | } 58 | 59 | function compact-log { 60 | process { 61 | $res = $_.Trim() 62 | if ( ($res -eq '') -or ($res -eq 'VPN>') -or ($res -eq $prev) ) { return } 63 | else { $prev = $res; $res} 64 | } 65 | } 66 | <# 67 | .SYNOPSIS 68 | Disconnect from the VPN network 69 | #> 70 | function disconnect-vpn() { 71 | "Disconnecting from VPN network" 72 | vpncli disconnect | compact-log 73 | } 74 | 75 | 76 | function find-anyconnect() { 77 | if ((gcm vpncli,vpnui -ea 0).count -eq 2) { return } 78 | 79 | Get-ApplicationUninstallKey "cisco anyconnect" | select -First 1 -expand UninstallString | set ac 80 | if ($ac) { $ac = Split-Path $ac } 81 | if (!(Test-Path $ac)) { 82 | $ac = "c:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client" 83 | if (!(Test-Path $ac)) { $ac = "c:\Program Files\Cisco\Cisco AnyConnect Secure Mobility Client" } 84 | if (!(Test-Path $ac)) { throw 'Unable to find Cisco AnnyConnect Secure Mobility Client' } 85 | } 86 | 87 | sal -scope global vpncli "$ac\vpncli.exe" 88 | sal -scope global vpnui "$ac\vpnui.exe" 89 | } 90 | 91 | function Get-ApplicationUninstallKey([string]$AppName) 92 | { 93 | $local_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' 94 | $machine_key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' 95 | $machine_key6432 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' 96 | gp @($machine_key6432, $machine_key, $local_key) -ea 0 | ? { $_.DisplayName -like "*$AppName*" } 97 | } 98 | 99 | sal vpnd disconnect-vpn 100 | sal vpnc connect-vpn 101 | find-anyconnect 102 | -------------------------------------------------------------------------------- /MM_FileSystem/Copy-ItemWithProgress.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | RoboCopy with PowerShell progress. 5 | 6 | .DESCRIPTION 7 | Performs file copy with RoboCopy. Output from RoboCopy is captured, 8 | parsed, and returned as Powershell native status and progress. 9 | 10 | .PARAMETER RobocopyArgs 11 | List of arguments passed directly to Robocopy. 12 | Must not conflict with defaults: /ndl /TEE /Bytes /NC /nfl /Log 13 | 14 | .OUTPUTS 15 | Returns an object with the status of final copy. 16 | REMINDER: Any error level below 8 can be considered a success by RoboCopy. 17 | 18 | .EXAMPLE 19 | C:\PS> Copy-ItemWithProgress c:\Src d:\Dest 20 | 21 | Copy the contents of the c:\Src directory to a directory d:\Dest 22 | Without the /e or /mir switch, only files from the root of c:\src are copied. 23 | 24 | .EXAMPLE 25 | C:\PS> Copy-ItemWithProgress '"c:\Src Files"' d:\Dest /mir /xf *.log -Verbose 26 | 27 | Copy the contents of the 'c:\Name with Space' directory to a directory d:\Dest 28 | /mir and /XF parameters are passed to robocopy, and script is run verbose 29 | 30 | .LINK 31 | http://keithga.wordpress.com/2014/06/23/copy-itemwithprogress 32 | 33 | .NOTES 34 | By Keith S. Garner (KeithGa@KeithGa.com) - 6/23/2014 35 | With inspiration by Trevor Sullivan @pcgeek86 36 | 37 | #> 38 | function Copy-ItemWithProgress { 39 | [CmdletBinding()] 40 | param( 41 | [Parameter(Mandatory = $true,ValueFromRemainingArguments=$true)] 42 | [string[]] $RobocopyArgs 43 | ) 44 | 45 | $ScanLog = [IO.Path]::GetTempFileName() 46 | $RoboLog = [IO.Path]::GetTempFileName() 47 | $ScanArgs = $RobocopyArgs + "/ndl /TEE /bytes /Log:$ScanLog /nfl /L".Split(" ") 48 | $RoboArgs = $RobocopyArgs + "/ndl /TEE /bytes /Log:$RoboLog /NC".Split(" ") 49 | 50 | # Launch Robocopy Processes 51 | write-verbose ("Robocopy Scan:`n" + ($ScanArgs -join " ")) 52 | write-verbose ("Robocopy Full:`n" + ($RoboArgs -join " ")) 53 | $ScanRun = start-process robocopy -PassThru -WindowStyle Hidden -ArgumentList $ScanArgs 54 | $RoboRun = start-process robocopy -PassThru -WindowStyle Hidden -ArgumentList $RoboArgs 55 | 56 | # Parse Robocopy "Scan" pass 57 | $ScanRun.WaitForExit() 58 | $LogData = get-content $ScanLog 59 | if ($ScanRun.ExitCode -ge 10) 60 | { 61 | $LogData|out-string|Write-Error 62 | throw "Robocopy $($ScanRun.ExitCode)" 63 | } 64 | $FileSize = [regex]::Match($LogData[-4],".+:\s+(\d+)\s+(\d+)").Groups[2].Value 65 | write-verbose ("Robocopy Bytes: $FileSize `n" +($LogData -join "`n")) 66 | 67 | # Monitor Full RoboCopy 68 | while (!$RoboRun.HasExited) 69 | { 70 | $LogData = get-content $RoboLog 71 | $Files = $LogData -match "^\s*(\d+)\s+(\S+)" 72 | if ($Files -ne $Null ) 73 | { 74 | $copied = ($Files[0..($Files.Length-2)] | %{$_.Split("`t")[-2]} | Measure -sum).Sum 75 | if ($LogData[-1] -match "(100|\d?\d\.\d)\%") 76 | { 77 | write-progress Copy -ParentID $RoboRun.ID -percentComplete $LogData[-1].Trim("% `t") $LogData[-1] 78 | $Copied += $Files[-1].Split("`t")[-2] /100 * ($LogData[-1].Trim("% `t")) 79 | } 80 | else 81 | { 82 | write-progress Copy -ParentID $RoboRun.ID -Completed 83 | } 84 | $PercentComplete = [math]::min(100,(100*$Copied/[math]::max($Copied,$FileSize))) 85 | write-progress ROBOCOPY -ID $RoboRun.ID -PercentComplete $PercentComplete $Files[-1].Split("`t")[-1] 86 | } 87 | } 88 | 89 | write-progress Copy -ParentID $RoboRun.ID -Completed 90 | write-progress Copy -ID $RoboRun.ID -Completed 91 | 92 | # Parse full RoboCopy pass results, and cleanup 93 | (get-content $RoboLog)[-11..-2] | out-string | Write-Verbose 94 | [PSCustomObject]@{ ExitCode = $RoboRun.ExitCode } 95 | remove-item $RoboLog, $ScanLog 96 | } 97 | -------------------------------------------------------------------------------- /MM_FileSystem/Update-FileAssociation.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 16-Jun-2015. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Associate file extensions to file types and commands 9 | 10 | .DESCRIPTION 11 | Update-FileAssociation is powershell implementation of cmd.exe commands assoc and ftype 12 | in single command. If only Extension is specified as the argument, the function returns 13 | assocition information for given array of extensions. If either FileType or Command are 14 | specified, the given attribute will be updated. 15 | 16 | .EXAMPLE 17 | assoc temp tempFile "`"$((gcm gvim).Definition)`" --remote-tab-silent %1" | fl 18 | 19 | Associate .temp extension to gvim and try to open files in a new tab of existing instance. 20 | 21 | .EXAMPLE 22 | assoc html,txt | select -Expand Executable 23 | 24 | Get the list of associated programs for given extensions. 25 | 26 | .NOTES 27 | Setting file association requires eleveated privileges. 28 | #> 29 | function Update-FileAssociation 30 | { 31 | param( 32 | # Extension (with or without the dot) for which to get/set file type and associated command. 33 | [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] 34 | [string[]]$Extension, 35 | 36 | # File type to set for extension (if != $null). To erase specify empty string. 37 | [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$false, Position=1)] 38 | [string]$FileType, 39 | 40 | # Command to set for extension (if != $null). To erase specify empty string. 41 | [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$false, Position=2)] 42 | [string]$Command 43 | ) 44 | 45 | $ErrorActionPreference = "Stop" 46 | New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ea ignore | Out-Null 47 | 48 | $result = "" | select Extension, FileType, Command, Executable 49 | $results = @() 50 | 51 | function parse-command {$args[0]} 52 | $set = "FileType","Command" | ? {$PSBoundParameters.Keys -contains $_ } 53 | foreach ($ext in $Extension) 54 | { 55 | $r = $result.PSObject.Copy() 56 | if (!$ext.StartsWith('.')) {$ext = ".$ext"} 57 | 58 | $r.Extension = $ext 59 | 60 | Write-Verbose "Reading registry for extension: $ext" 61 | $ftype = gp "HKCR:\$ext" -ea ignore 62 | if ($ftype) { 63 | $r.FileType = $ftype.'(default)' 64 | if ($r.FileType) { 65 | Write-Verbose "Reading registry for file type: $($r.FileType)" 66 | $assoc = gp "HKCR:\$($r.FileType)\shell\open\command" -ea ignore 67 | if ($assoc){ $r.Command = $assoc.'(default)' } 68 | } 69 | } 70 | 71 | if ($set) { 72 | if ($FileType -ne $null) { 73 | Write-Verbose "Setting file type for file extension: $ext" 74 | "HKCR:\$ext" | % { 75 | mkdir $_ -ea ignore | Out-Null 76 | sp $_ '(default)' ($r.FileType = $FileType) 77 | } 78 | } 79 | if ($Command -ne $null) { 80 | Write-Verbose "Setting command for the file extension: $ext" 81 | "HKCR:\$FileType\shell\open\command" | % { 82 | mkdir $_ -force -ea ignore| Out-Null 83 | sp $_ '(default)' ($r.Command = $Command) 84 | } 85 | } 86 | } 87 | 88 | $r.Executable = iex "parse-command $($r.Command)" 89 | $results += $r 90 | } 91 | $results 92 | } 93 | 94 | sal assoc Update-FileAssociation 95 | 96 | # Relevant docs 97 | #https://msdn.microsoft.com/en-us/library/windows/desktop/ms724498(v=vs.85).aspx 98 | #http://stackoverflow.com/questions/3924753/where-does-windows-store-its-open-with-settings 99 | -------------------------------------------------------------------------------- /MM_TotalCmd/totalcmd.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Author: Miodrag Milic 3 | This script contains various Total Commander functions 4 | #> 5 | 6 | function Test-Commander { 7 | if (!($Env:COMMANDER_PATH -and (Test-Path $Env:COMMANDER_PATH))) { throw 'This package requires COMMANDER_PATH environment variable set' } 8 | 9 | if (ps totalcmd* -ea 0) { 10 | Write-Warning "Total Commander is running; restart it for any changes to take effect" 11 | } 12 | } 13 | 14 | function Get-TCInstallPath { 15 | function check($path) { (gi $path\totalcmd.exe -ea 0).VersionInfo.InternalName -eq 'TOTALCMD' } 16 | 17 | $res = $Env:COMMANDER_PATH 18 | if ( check $res ) { return $res } 19 | 20 | $res = (gp 'HKCU:\Software\Ghisler\Total Commander' -ea 0).InstallDir 21 | if ( check $res ) { return $res } 22 | 23 | $res = (gp 'HKLM:\Software\Ghisler\Total Commander' -ea 0).InstallDir 24 | if ( check $res ) { return $res } 25 | } 26 | 27 | function Get-TCIniPath { 28 | 29 | $res = $Env:COMMANDER_INI 30 | if ( $res -and (Test-Path $res)) { return $res } 31 | 32 | $res = (gp 'HKCU:\Software\Ghisler\Total Commander' -ea 0).IniFileName 33 | if ( $res -and (Test-Path $res)) { return $res } 34 | 35 | $res = (gp 'HKLM:\Software\Ghisler\Total Commander' -ea 0).IniFileName 36 | if ( $res -and (Test-Path $res)) { return $res } 37 | 38 | $res = "$Env:AppData\Ghisler\wincmd.ini" 39 | if ( $res -and (Test-Path $res)) { return $res } 40 | 41 | $res = "$Env:WinDir\wincmd.ini" 42 | if ( $res -and (Test-Path $res)) { return $res } 43 | } 44 | 45 | function Install-TCPlugin( [string] $Path, [string] $Name ) { 46 | 47 | $plugin_name = Split-Path $Path -Leaf 48 | $plugins_path = "$Env:COMMANDER_PATH\plugins" 49 | mkdir $plugins_path -ea 0 | Out-Null 50 | 51 | $tmpDestination = "$Env:TEMP\_tcp\$plugin_name" 52 | rm $tmpDestination -Recurse -ea 0 53 | 7z x $Path "-o$tmpDestination" 54 | if ($LastExitCode) { throw "Error while unpacking plugin: $LastExitCode" } 55 | 56 | $plugin_types = 'wfx', 'wlx', 'wcx', 'wdx' 57 | $plugin_type = $plugin_types | ? { ([array](ls $tmpDestination\* -Include *$_*)).Count -gt 0 } | select -First 1 58 | if (!$plugin_type) { throw "Plugin type must be one of the: $plugin_types" } 59 | 60 | $plugin_path = "$plugins_path\$plugin_type\$Name" 61 | 62 | Write-Host "Installing Total Commander plugin files at: $plugin_path" 63 | 64 | rm $plugin_path -Recurse -Force -ea 0 65 | mkdir $plugin_path -ea 0 | Out-Null 66 | mv $tmpDestination\* $plugin_path -Force 67 | 68 | if (!($iniPath = Get-TCIniPath)) { throw "Can't find Total Commander ini path" } 69 | 70 | Write-Host "Adding plugin to ini file: $iniPath" 71 | $iniContent = gc $iniPath -Encoding UTF8 -Raw 72 | 73 | if ($plugin_type -in 'wfx','wcx') { 74 | $iniContent | Set-IniValue FileSystemPlugins $Name $plugin_path\$Name.$plugin_type ` 75 | | Set-IniValue FileSystemPlugins64 $Name 1 ` 76 | | Save-Content $iniPath 77 | } else { 78 | throw "This plugin type is not yet supported" 79 | } 80 | 81 | # 'FileSystemPlugins' Name=Path 82 | # 'PackerPlugins' Name=Path 83 | # 'ListerPlugins' No=Path 84 | # 'ContentPlugins' No=Path 85 | } 86 | 87 | function Save-Content([string] $Path, [Parameter(ValueFromPipeline=$true)] [string] $Text) { 88 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) 89 | [System.IO.File]::WriteAllText($Path, $Text, $Utf8NoBomEncoding) 90 | } 91 | 92 | 93 | function Uninstall-TCPlugin( [string] $Name ) { 94 | Write-Host "Removing Total Commander plugin files: $Name" 95 | rm $Env:COMMANDER_PATH\plugins\*\$Name -Recurse -Force 96 | 97 | Write-Host "Removing Total Commander ini key" 98 | if (!($iniPath = Get-TCIniPath)) { throw "Can't find Total Commander ini path" } 99 | $iniContent = gc $iniPath -Encoding UTF8 -Raw 100 | $iniContent | Set-IniValue FileSystemPlugins $Name | Save-Content $iniPath 101 | } 102 | 103 | Test-Commander -------------------------------------------------------------------------------- /MM_Admin/Start-ElevatedProcess.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 22-Oct-2016. 3 | 4 | #requires -version 2 5 | 6 | <# 7 | .SYNOPSIS 8 | Runs a process / command as admin. 9 | 10 | .DESCRIPTION 11 | Runs a process with elevated privileges. Has the ability to open a new 12 | powershell window. Can run legacy programs as admin cmd. 13 | 14 | .EXAMPLE 15 | sudo 16 | 17 | Opens a new powershell window with the administrative privileges and sets 18 | the current directory to the one of the calling shell. 19 | 20 | .EXAMPLE 21 | sudo -Last -Exit 22 | 23 | Runs the last powershell command with the adminsitrative privileges and 24 | keeps the shell from closing. 25 | 26 | .EXAMPLE 27 | Start-ElevatedProcess {ps iexplore | kill} -Wait -WindowStyle Hidden 28 | 29 | Opens a new powershell window with administrative privileges to stops all 30 | internet explorer process. Wait for action to return but hide the window. 31 | 32 | .EXAMPLE 33 | Start-ElevatedProcess {C:\Windows\System32\Drivers\etc\hosts} -Program notepad 34 | 35 | Opens the host file as admin in notepad.exe. 36 | 37 | #> 38 | function Start-ElevatedProcess { 39 | [CmdletBinding(DefaultParameterSetName='Default')] 40 | param( 41 | # Optional script block to execute or a program argument 42 | [Parameter(Position=0, ParameterSetName='command')] 43 | [scriptblock] $Command, 44 | 45 | # Optional application to elevate, by default 'powershell.exe'. 46 | [Parameter(Position=1)] 47 | [string] $Program = (Join-Path -Path $PsHome -ChildPath 'powershell.exe'), 48 | 49 | # Path to script file, Program must be 'powershell.exe' or 'cmd.exe'. 50 | [Parameter(Position=0, ParameterSetName='script')] 51 | [ValidateScript({if(Test-Path -Path $_ -PathType Leaf){ $true } else{Throw "$_ is not a valid Path"}})] 52 | [string] $Script, 53 | 54 | # Run previous powershell command as admin. Commands starting with 'sudo' and 'Start-ElevatedProcess' are ignored. 55 | [Parameter(ParameterSetName='last')] 56 | [switch] $Last, 57 | 58 | # Close the eveleated shell when finished. 59 | [Parameter(ParameterSetName='last')] 60 | [Parameter(ParameterSetName='command')] 61 | [Parameter(ParameterSetName='script')] 62 | [switch] $Exit, 63 | 64 | # Wait for process to exit before returning 65 | [switch] $Wait, 66 | 67 | # Window style: Normal, Maximized, Minimized or Hidden 68 | [ValidateSet('Normal','Maximized','Minimized','Hidden')] 69 | [string] $WindowStyle = 'Normal' 70 | ) 71 | 72 | $params = @{ 73 | #WorkingDirectory = $pwd #doesn't work with RunAs verb so I used 'cd $pwd' with command 74 | FilePath = $Program 75 | Verb = 'RunAs' 76 | ErrorAction = 'Stop' 77 | Wait = $false 78 | WindowStyle = $WindowStyle 79 | } 80 | 81 | $argList = @() 82 | if ($program -match '[\\]?powershell(.exe)?$') { 83 | if (!$Exit) { $argList += '-NoExit' } 84 | 85 | $cmd = '-Command "' + "cd '$pwd'" + '"' 86 | if ($Command) { $cmd += "; {0}" -f $Command } 87 | if ($Last) { 88 | $l = h | sort -Desc | ? { $_.CommandLine -notmatch '^\s*(sudo|Start-ElevatedProcess)\s*' } | select -First 1 -Expand CommandLine 89 | $cmd += "; {0}" -f $l 90 | } 91 | $argList += $cmd 92 | 93 | if ($Script) { $argList += "-File ""{0}""" -f (Resolve-Path $script) } 94 | } 95 | elseif($program -match '[\\]?cmd(.exe)?$'){ 96 | $a = '/K'; 97 | if ($Exit) { $a = '/C' } 98 | if ($Command) { $argList += "$a ""cd ""$pwd"" & $command""" } 99 | if ($Script) { $argList += "$a ""{0}""" -f (Resolve-Path $script) } 100 | } 101 | else { 102 | if ($Command) { $argList += $Command } 103 | } 104 | 105 | if ($Wait) { $params.Wait = $true } 106 | 107 | if ($argList -and $argList.Count) { $params.ArgumentList = $argList } 108 | Write-Verbose $($params | out-string) 109 | Start-Process @params 110 | } 111 | -------------------------------------------------------------------------------- /MM_Network/Update-Proxy.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 14-Aug-2015. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Get or set system proxy properties. 9 | 10 | .DESCRIPTION 11 | This function implements unified method to set proxy system wide settings. 12 | It sets both WinINET ("Internet Options" proxy) and WinHTTP proxy. 13 | Without any arguments function will return the current proxy properties. 14 | To change a proxy property pass adequate argument to the function. 15 | 16 | .EXAMPLE 17 | Update-Proxy -Server "myproxy.mydomain.com:8080" -Override "" -ShowGUI 18 | 19 | Set proxy server, clear overrides and show IE GUI. 20 | 21 | .EXAMPLE 22 | Update-Proxy | Export-CSV proxy; Import-CSV proxy | Update-Proxy -Verbose 23 | 24 | Save and restore proxy properties 25 | 26 | .EXAMPLE 27 | $p = Update-Proxy; $p.Override += $p.Override += "*.domain.com" ; $p | proxy 28 | 29 | Add "*.domain.com" to the proxy override list 30 | 31 | .NOTES 32 | The format of the parameters is the same as seen in Internet Options GUI. 33 | To bypass proxy for a local network specify keyword ";" at the end 34 | of the Overide values. 35 | Setting the winhttp proxy requires administrative prvilegies. 36 | 37 | .OUTPUTS 38 | [HashTable] 39 | #> 40 | function Update-Proxy() { 41 | [CmdletBinding()] 42 | param( 43 | # Proxy:Port 44 | [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 45 | [string] $Server, 46 | # Semicollon delimited list of exlusions 47 | [Parameter(ValueFromPipelineByPropertyName=$true)] 48 | [string] $Override, 49 | # 0 to disable, anything else to enable proxy 50 | [Parameter(ValueFromPipelineByPropertyName=$true)] 51 | [string] $Enable, 52 | # Show Internet Options GUI 53 | [switch] $ShowGUI 54 | ) 55 | $key = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" 56 | $r = gp $key 57 | Write-Verbose "Reading proxy data from the registry" 58 | $proxy=@{ 59 | Server = if ($PSBoundParameters.Keys -contains 'Server') {$Server} else { $r.ProxyServer } 60 | Override = if ($PSBoundParameters.Keys -contains 'Override') {$Override} else { $r.ProxyOverride } 61 | Enable = if ($PSBoundParameters.Keys -contains 'Enable') {$Enable} else { $r.ProxyEnable } 62 | } 63 | 64 | $set = "Server","Override","Enable" | ? {$PSBoundParameters.Keys -contains $_ } 65 | if ($set) { 66 | #if (!(test-admin)) { throw "Setting proxy requires admin privileges" } 67 | 68 | Write-Verbose "Saving proxy data to registry" 69 | 70 | sp $key ProxyServer $proxy.Server 71 | sp $key ProxyOverride $proxy.Override 72 | sp $key ProxyEnable $proxy.Enable 73 | if (!(refresh-system)) { Write-Warning "Can not force system refresh after proxy change" } 74 | 75 | Write-Verbose "Importing winhttp proxy from IE settings" 76 | $OFS = "`n" 77 | [string]$res = netsh.exe winhttp import proxy source=ie 78 | if ($res -match 'Access is denied') {Write-Warning $res} 79 | else { Write-Verbose $res.Trim()} 80 | } 81 | 82 | New-Object PSCustomObject -Property $proxy 83 | if ($ShowGUI) { start control "inetcpl.cpl,,4" } 84 | } 85 | 86 | # The registry changes aren't seen until system is notified about it. 87 | # Without this function you need to open Internet Settings window for changes to take effect. See http://goo.gl/OIQ4W4 88 | function refresh-system() { 89 | $signature = @' 90 | [DllImport("wininet.dll", SetLastError = true, CharSet=CharSet.Auto)] 91 | public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); 92 | '@ 93 | 94 | $INTERNET_OPTION_SETTINGS_CHANGED = 39 95 | $INTERNET_OPTION_REFRESH = 37 96 | $type = Add-Type -MemberDefinition $signature -Name wininet -Namespace pinvoke -PassThru 97 | $a = $type::InternetSetOption(0, $INTERNET_OPTION_SETTINGS_CHANGED, 0, 0) 98 | $b = $type::InternetSetOption(0, $INTERNET_OPTION_REFRESH, 0, 0) 99 | return $a -and $b 100 | } 101 | 102 | Set-Alias proxy Update-Proxy 103 | -------------------------------------------------------------------------------- /MM_Network/Update-CLIProxy.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 25-Jun-2015. 3 | 4 | #requires -version 2.0 5 | 6 | <# 7 | .SYNOPSIS 8 | Show or Update proxy environment variables from the system proxy settings. 9 | 10 | .DESCRIPTION 11 | The function updates Linux like HTTP_PROXY and related environment variables with the current system proxy settings. 12 | Without any parameters it will show current values. 13 | 14 | .OUTPUTS 15 | Returns string that is convenient to use as Powershell variable definition so that you can export the result of the 16 | function to be used elsewere: Update-CLIProxy | out-file proxy_vars.ps1 17 | 18 | .EXAMPLE 19 | Update-CLIProxy -FromSystem -Verbose 20 | 21 | Update proxy environment variables in the current session (no -Register option). 22 | 23 | .EXAMPLE 24 | proxyc -Clear -Register -Verbose 25 | 26 | Clear proxy CLI variables and remove them from the system 27 | 28 | .NOTES 29 | Linux doesn't support setting globs (*) for NO_PROXY variable like Windows. If the same exclusions should work both with Windows 30 | and Linux tools, simply mix definitions and each tool will understand what it can. Additionally, delimiter for proxy 31 | exclusions on Windows is `;` and on Linux `,` which this function automatically handles. Keep this in mind in case you need 32 | to load Windows proxy settings from NO_PROXY variable previously created with this function. 33 | If the system proxy is disabled, the function will clear all variables just the same as with parameter Clear. 34 | For more info see http://goo.gl/ZUD2tC. 35 | #> 36 | function Update-CLIProxy() 37 | { 38 | [CmdletBinding()] 39 | param ( 40 | # Register enviornment variables in the system. Without this flag environment variables are local only. 41 | # Requires administrative rights. Must be used with Clear or FromSystem parameters. 42 | [switch] $Register, 43 | # Create environment variables from the system settings. If the system proxy properties are populated but 44 | # the proxy is disabled, this option will clear environment variables. 45 | [switch] $FromSystem, 46 | # Clear the environment variables for the current shell. Combine with the Register parameter, to unregister 47 | # envronment variables from the system. 48 | [switch] $Clear 49 | ) 50 | 51 | if ($Register) { 52 | if (!(test-admin)) { throw "Setting system environment requires admin privileges" } 53 | else { Write-Verbose "Remembering changes in the system environment" } 54 | } 55 | 56 | $proxy_vars = "http_proxy", "https_proxy", "ftp_proxy" 57 | 58 | if ($FromSystem -and !$Clear) { 59 | Write-Verbose "Setting proxy environment variables." 60 | 61 | $proxy = Update-Proxy 62 | if ($proxy.Enable -eq 0) { 63 | Write-Verbose "Proxy disabled, setting Clear flag" 64 | $Clear = $true 65 | } 66 | 67 | if (!$Clear) { 68 | if ($proxy.Server) { $Env:http_proxy = "http://" + $proxy.Server } 69 | $proxy_vars | % { 70 | Set-Item Env:$_ $Env:http_proxy 71 | if ($Register) { [Environment]::SetEnvironmentVariable($_, $Env:http_proxy, "Machine") } 72 | } 73 | 74 | $Env:no_proxy = $proxy.Override.Replace(";",",").Replace('*','') # linux format 75 | 76 | if ($Register) { [Environment]::SetEnvironmentVariable("no_proxy", $Env:no_proxy, "Machine") } 77 | } 78 | } 79 | 80 | if ($Clear) { 81 | Write-Verbose "Clearing proxy environment variables" 82 | $proxy_vars + "no_proxy" | % { 83 | Set-Item Env:$_ $null 84 | if ($Register) { [Environment]::SetEnvironmentVariable($_, $null, "Machine") } 85 | } 86 | } 87 | 88 | $env = @("Env:no_proxy") 89 | $proxy_vars | % { $env += "Env:$_" } 90 | 91 | $env | sort | % { "`${0,-15:0} = '{1}'" -f $_, (gi "$_" -ea SilentlyContinue).Value } 92 | } 93 | 94 | function test-admin() { 95 | $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() 96 | $usercontext.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 97 | } 98 | 99 | Set-Alias proxyc Update-CLIProxy 100 | -------------------------------------------------------------------------------- /MM_Network/Connect-WirelessNetwork.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 25-Jun-2015. 3 | 4 | #requires -version 3 5 | 6 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms707381(v=vs.85).aspx 7 | <# 8 | .SYNOPSIS 9 | Connect to wireless network 10 | 11 | .DESCRIPTION 12 | Connect to choosen wireless network. Create profile if it doesn't exist already. 13 | 14 | .EXAMPLE 15 | Get-WirlessNetwork android | Connect-WirelessNetwork 16 | 17 | Connect to wireless network with preexisting profile 18 | .EXAMPLE 19 | Get-WirlessNetwork android | Connect-WirelessNetwork -Key 1234567890 -Force 20 | 21 | Connect to wireless network and force creation of new profile with specified key 22 | #> 23 | function Connect-WirelessNetwork() { 24 | [CmdletBinding()] 25 | param( 26 | # Wireless network name 27 | [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 28 | [string]$SSID, 29 | 30 | #Passphrase or a network key 31 | [Parameter(ValueFromPipelineByPropertyName=$true)] 32 | [string]$Key, 33 | 34 | # Specifies the authentication method that must be used to connect to the wireless LAN 35 | [Parameter(ValueFromPipelineByPropertyName=$true)] 36 | [ValidateSet('open', 'shared', 'WPA-Personal', 'WPA2-Personal')] #WPA-Enterprise, WPA2-Enterprise 37 | [string]$Authentication='WPA2-Personal', 38 | 39 | # Sets the data encryption to use to connect to the wireless LAN 40 | [Parameter(ValueFromPipelineByPropertyName=$true)] 41 | [ValidateSet('none','WEP','TKIP','CCMP')] 42 | [string]$Encryption='CCMP', 43 | 44 | # ConnectionType 45 | [Parameter(ValueFromPipelineByPropertyName=$true)] 46 | [ValidateSet('ESS','IBSS')] 47 | [string]$ConnectionType='ESS', 48 | 49 | # Indicates whether connection to a wireless LAN should be automatic or initiated by user 50 | [Parameter(ValueFromPipelineByPropertyName=$true)] 51 | [ValidateSet('auto','manual')] 52 | [string]$ConnectionMode = 'auto', 53 | 54 | # Force profile creation 55 | [switch]$Force 56 | ) 57 | if (!$SSID) { throw "SSID must be specified" } 58 | 59 | netsh wlan show profile $SSID | out-null 60 | if ($LastExitCode -or $Force) { 61 | Write-Verbose "Creating profile $SSID" 62 | $auth = $Authentication -replace '-Personal', 'PSK' 63 | $enc = $Encryption -replace 'CCMP', 'AES' 64 | if ($ConnectionType -eq 'IBSS') {$ConnectionMode = 'manual'} 65 | 66 | $keyType = 'passPhrase' 67 | if ($Encryption -eq 'WEP') { $keyType = 'networkKey' } 68 | 69 | $profile_xml=@" 70 | 71 | 72 | $SSID 73 | 74 | 75 | $SSID 76 | 77 | 78 | $ConnectionType 79 | $ConnectionMode 80 | 81 | 82 | 83 | $auth 84 | $enc 85 | 86 | 87 | $keyType 88 | false 89 | $Key 90 | 91 | 92 | 93 | 94 | "@ 95 | #netsh wlan delete profile name=$SSID | out-null 96 | netsh wlan disconnect 97 | #$profile_fn = "$Env:Temp\tmp_profile.xml" 98 | $profile_fn = "tmp_profile.xml" 99 | $profile_xml | out-file $profile_fn 100 | netsh wlan add profile filename=$profile_fn 101 | if ($LastExitCode) {return} 102 | rm $profile_fn #possibly overwrite this file to shred the password content 103 | } else { Write-Verbose "Using existing profile for $SSID" } 104 | 105 | netsh wlan connect name=$SSID #interface is mandatory if there are multiple 106 | } 107 | 108 | #Store: "$Env:ProgramData\Microsoft\WlanSvc\Profiles\Interfaces" 109 | #If unencrypted key material is passed to WlanSetProfile, the key material is automatically encrypted before it is stored in the profile store. 110 | -------------------------------------------------------------------------------- /MM_VPN/README.md: -------------------------------------------------------------------------------- 1 | MM_VPN 2 | ====== 3 | 4 | This module implements functions to connect to the VPN network using Powershell and Cisco Anyconnect. 5 | 6 | Prerequisites 7 | ------------- 8 | 9 | - [Cisco AnyConnect client](http://www.cisco.com/c/en/us/support/security/anyconnect-secure-mobility-client/tsd-products-support-series-home.html) 10 | - Powershell 3+ (Windows 7 only)( `cinst powershell` ) 11 | - Enable script execution via `Set-ExecutionPolicy` 12 | 13 | Installation 14 | ------------ 15 | 16 | [Install module](https://msdn.microsoft.com/en-us/library/dd878350(v=vs.85).aspx) in some of the designated folders. 17 | 18 | Usage 19 | ----- 20 | 21 | Create `myvpn.ps1` script file which will will hold the VPN connections. Source this script in your $PROFILE: 22 | 23 | . \myvpn.ps1 24 | 25 | To define connections within `myvpn.ps1` import `mm_vpn` module and define function for each VPN network: 26 | 27 | **Example of myvpn.ps1 script**: 28 | 29 | import-module mm_vpn 30 | 31 | function acme() { connect-vpn $PSScriptRoot/acme } 32 | function ibm() { connect-vpn $PSScriptRoot/ibm } 33 | 34 | In above case two connections are defined, `acme` and `ibm` which use configuration files defined in the same directory as `myvpn.ps1` script. 35 | After this, to connect to the network simply call the function from within console: 36 | 37 | PS> acme 38 | Connecting to VPN network using configuration 'acme' 39 | Cisco AnyConnect Secure Mobility Client (version 4.2.00096) 40 | ... 41 | 42 | Complete cumulative log file is saved in the file `vpn.log` in the module directory. 43 | 44 | Functions 45 | --------- 46 | 47 | Module defines two functions: 48 | 49 | - `connect-vpn $config -Timeout $seconds` (alias vpnc) 50 | Connect to the specified VPN network using given $config file path. By default, Timeout parameter is -1 which means that function will wait forever for the vpn client to return which might be problematic if connection parameters change on the server as it will result that any connect client stays in its REPL mode. To prevent this, specify Timeout parameter for function to terminate the client after desired wait time. 51 | - `disconnect-vpn` (alias vpnd) 52 | Disconnect from the VPN network. 53 | 54 | 55 | Configuration file 56 | ------------------ 57 | 58 | This is the simple text file where each line is the command or response that VPN client expects from the user. If the client stays in the REPL mode with given configuration that means that specified lines are incorrect in given context and should be fixed by executing `vpncli` and recording the correct sequence of answers given to the client in order to connect. 59 | 60 | **Example** 61 | 62 | connect vpn.acme.com 63 | 3 64 | my_user 65 | my_password 66 | 67 | In any case, you should edit the configuration with your username and password. 68 | 69 | Complete output example 70 | ----------------------- 71 | 72 | This is the output when connecting to the VPN network "acme" with 60 seconds timeout: 73 | 74 | PS> vpnc $pwd\myvpn\acme 60 75 | -------------------------------------------------- 76 | Connect-VPN started at 2016-02-11T13-18-45 77 | Started background vpn connection: acme 78 | 79 | Cisco AnyConnect Secure Mobility Client (version 3.1.12020) . 80 | Copyright (c) 2004 - 2015 Cisco Systems, Inc. All Rights Reserved. 81 | >> state: Disconnected 82 | >> state: Disconnected 83 | >> notice: Ready to connect. 84 | >> registered with local VPN subsystem. 85 | >> contacting host (vpn.acme.com) for login information... 86 | >> notice: Contacting vpn.acme.com. 87 | >> Please enter your username and password. 88 | 0) Cert-Full 89 | 1) Cert-VPN 90 | 2) Sec-LAIR 91 | 3) VPN 92 | Group: [VPN] 93 | Username: Password: 94 | >> state: Connecting 95 | >> notice: Establishing VPN session... 96 | >> notice: Checking for profile updates... 97 | >> notice: Checking for product updates... 98 | >> notice: Checking for customization updates... 99 | >> notice: Performing any required updates... 100 | >> state: Connecting 101 | >> notice: Establishing VPN session... 102 | >> notice: Establishing VPN - Initiating connection... 103 | >> notice: Establishing VPN - Examining system... 104 | >> notice: Establishing VPN - Activating VPN adapter... 105 | >> notice: Establishing VPN - Configuring system... 106 | >> notice: Establishing VPN... 107 | >> state: Connected 108 | VPN> goodbye... 109 | >> note: VPN Connection is still active 110 | -------------------------------------------------------------------------------- /MM_Network/Test-URI.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 4.0 2 | 3 | Function Test-URI { 4 | <# 5 | .Synopsis 6 | Test a URI or URL 7 | .Description 8 | This command will test the validity of a given URL or URI that begins with either http or https. The default behavior is to write a Boolean value to the pipeline. But you can also ask for more detail. 9 | 10 | Be aware that a URI may return a value of True because the server responded correctly. For example this will appear that the URI is valid. 11 | 12 | test-uri -uri http://files.snapfiles.com/localdl936/CrystalDiskInfo7_2_0.zip 13 | 14 | But if you look at the test in detail: 15 | 16 | ResponseUri : http://files.snapfiles.com/localdl936/CrystalDiskInfo7_2_0.zip 17 | ContentLength : 23070 18 | ContentType : text/html 19 | LastModified : 1/19/2015 11:34:44 AM 20 | Status : 200 21 | 22 | You'll see that the content type is Text and most likely a 404 page. By comparison, this is the desired result from the correct URI: 23 | 24 | PS C:\> test-uri -detail -uri http://files.snapfiles.com/localdl936/CrystalDiskInfo6_3_0.zip 25 | 26 | ResponseUri : http://files.snapfiles.com/localdl936/CrystalDiskInfo6_3_0.zip 27 | ContentLength : 2863977 28 | ContentType : application/x-zip-compressed 29 | LastModified : 12/31/2014 1:48:34 PM 30 | Status : 200 31 | 32 | .Example 33 | PS C:\> test-uri https://www.petri.com 34 | True 35 | .Example 36 | PS C:\> test-uri https://www.petri.com -detail 37 | 38 | ResponseUri : https://www.petri.com/ 39 | ContentLength : -1 40 | ContentType : text/html; charset=UTF-8 41 | LastModified : 1/19/2015 12:14:57 PM 42 | Status : 200 43 | .Example 44 | PS C:\> get-content D:\temp\uris.txt | test-uri -Detail | where { $_.status -ne 200 -OR $_.contentType -notmatch "application"} 45 | 46 | ResponseUri : http://files.snapfiles.com/localdl936/CrystalDiskInfo7_2_0.zip 47 | ContentLength : 23070 48 | ContentType : text/html 49 | LastModified : 1/19/2015 11:34:44 AM 50 | Status : 200 51 | 52 | ResponseURI : http://download.bleepingcomputer.com/grinler/rkill 53 | ContentLength : 54 | ContentType : 55 | LastModified : 56 | Status : 404 57 | 58 | Test a list of URIs and filter for those that are not OK or where the type is not an application. 59 | .Notes 60 | Last Updated: January 19, 2015 61 | Version : 1.0 62 | 63 | https://www.petri.com/testing-uris-urls-powershell 64 | 65 | .Link 66 | Invoke-WebRequest 67 | #> 68 | 69 | [CmdletBinding(DefaultParameterSetName="Default")] 70 | Param( 71 | [Parameter(Position=0,Mandatory,HelpMessage="Enter the URI path starting with HTTP or HTTPS", ValueFromPipeline,ValueFromPipelineByPropertyName)] 72 | [ValidatePattern( "^(http|https)://" )] 73 | [Alias("url")] 74 | [string]$URI, 75 | 76 | [Parameter(ParameterSetName="Detail")] 77 | [Switch]$Detail, 78 | 79 | [ValidateScript({$_ -ge 0})] 80 | [int]$Timeout = 30 81 | ) 82 | 83 | Begin { 84 | Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" 85 | Write-Verbose -message "Using parameter set $($PSCmdlet.ParameterSetName)" 86 | } 87 | 88 | Process { 89 | Write-Verbose -Message "Testing $uri" 90 | Try { 91 | #hash table of parameter values for Invoke-Webrequest 92 | $paramHash = @{ 93 | UseBasicParsing = $True 94 | DisableKeepAlive = $True 95 | Uri = $uri 96 | Method = 'Head' 97 | ErrorAction = 'stop' 98 | TimeoutSec = $Timeout 99 | } 100 | $test = Invoke-WebRequest @paramHash 101 | 102 | if ($Detail) { 103 | $test.BaseResponse | Select ResponseURI,ContentLength,ContentType,LastModified, @{Name="Status";Expression={$Test.StatusCode}} 104 | } else { 105 | if ($test.statuscode -ne 200) { 106 | #it is unlikely this code will ever run but just in case 107 | Write-Verbose -Message "Failed to request $uri" 108 | write-Verbose -message ($test | out-string) 109 | $False 110 | } 111 | else { $True } 112 | } 113 | } 114 | Catch { 115 | #there was an exception getting the URI 116 | write-verbose -message $_.exception 117 | if ($Detail) { 118 | #most likely the resource is 404 119 | $objProp = [ordered]@{ 120 | ResponseURI = $uri 121 | ContentLength = $null 122 | ContentType = $null 123 | LastModified = $null 124 | Status = 404 125 | } 126 | #write a matching custom object to the pipeline 127 | New-Object -TypeName psobject -Property $objProp 128 | 129 | } else { $false } 130 | } 131 | } 132 | 133 | end { Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } 134 | } 135 | -------------------------------------------------------------------------------- /MM_Admin/Repair-Path.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 3 2 | #http://stackoverflow.com/questions/4405091/how-do-you-avoid-over-populating-the-path-environment-variable-in-windows 3 | 4 | <# 5 | Last Change: 12-Feb-2016. 6 | Author: M. Milic 7 | 8 | .SYNOPSIS 9 | Cleanup and minimize the machine PATH variable. 10 | 11 | .EXAMPLE 12 | PS> Repair-Path -Whatif 13 | #> 14 | function Repair-Path() { 15 | [CmdletBinding(SupportsShouldProcess=$True)] 16 | param( 17 | #Asks for each path whether to remove it 18 | [switch]$Interactive, 19 | #Convert paths to short names to make up more space 20 | [switch]$Shrink, 21 | #Convert all short paths to long names 22 | [switch]$Expand 23 | #[switch]$UseSymbolicLinks, 24 | #[switch]$RestoreBackup 25 | ) 26 | 27 | function check_path_len( $path ) { 28 | if ($path.length -gt 2048) { Write-Warning "Path length is longer then 2048 chars, some tools might not work correctly." } 29 | if ($path.length -gt 32767) { Write-Warning "Path length is longer then 32768 chars which is not supported by Windows."} 30 | } 31 | 32 | function choice () { 33 | $choices = [ordered]@{ 34 | keep = "&Keep", "Keep the path" 35 | remove = "&Remove", "Remove the path" 36 | stop = "&Stop", "Stop interactive mode" 37 | } 38 | 39 | $c = @() 40 | $choices.GetEnumerator() | % { $c += New-Object System.Management.Automation.Host.ChoiceDescription $_.Value[0], $_.Value[1] } 41 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($c[0], $c[1], $c[2]) 42 | $res = $host.ui.PromptForChoice($null, $v.dir, $options, 0) 43 | 44 | $choices.keys -split '\n'| select -Index $res 45 | } 46 | 47 | function double() { 48 | $d = $v.npath.ToLower() -match "$([Regex]::Escape($v.dir.ToLower()))$($v.sep)?" -or $v.npath.ToLower() -match "$([Regex]::Escape($v.edir.ToLower()))$($v.sep)?" 49 | if (!$d) { return $false } 50 | 51 | $s.Duplicates += 1 52 | Write-Verbose "Duplicate removed: $($v.dir)" 53 | return $true 54 | } 55 | 56 | function invalid() { 57 | if (Test-Path $v.edir) { return $false} 58 | 59 | $s.Removed += $v.dir 60 | Write-Verbose "Path doesn't exist: $($v.dir)" 61 | return $true 62 | } 63 | 64 | function show_stats() { 65 | "Path modified:" 66 | " Duplicates: $($s.Duplicates)" 67 | " Empty paths: $($s.Empty)" 68 | " Trailing separators: $($s.Trails)" 69 | " Non existent: $($s.Removed.length)" 70 | $s.Removed | % { " $_" } 71 | 72 | if ($Interactive) { 73 | "User selected: $($s.user.length)" 74 | $s.user | % { " $_" } 75 | } 76 | "Total paths: " + ($v.npath -split ';').Length 77 | "Old path length: $($v.path.length)" 78 | "New path length: $($v.npath.length)" 79 | "Gained: " + ($v.path.length - $v.npath.length) 80 | check_path_len $v.npath 81 | } 82 | 83 | function shrink($Path) { 84 | $code = @' 85 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)] 86 | public static extern uint GetShortPathName(string longPath, 87 | StringBuilder shortPath,uint bufferSize); 88 | '@ 89 | $API = Add-Type -MemberDefinition $code -Name Path -UsingNamespace System.Text -PassThru 90 | $shortBuffer = New-Object Text.StringBuilder ($Path.Length * 2) 91 | $rv = $API::GetShortPathName( $Path, $shortBuffer, $shortBuffer.Capacity ) 92 | if ($rv -ne 0) { 93 | $shortBuffer.ToString() 94 | } else { Write-Warning "Can't get short name for the path: $Path" } 95 | } 96 | 97 | function expand($path) { 98 | gi $path | select -ExpandProperty Fullname 99 | } 100 | 101 | function Test-Admin() { 102 | $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() 103 | $usercontext.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 104 | } 105 | 106 | # =============================================== 107 | 108 | $s=@{Removed = @(); User = @(); Duplicates = 0; Empty = 0; Trails = 0 } #stats 109 | $v=@{path=''; npath=''; sep=';'; dir=''; edir=''} #vars to share with nested funcs 110 | 111 | "" 112 | $v.path = [Environment]::GetEnvironmentVariable("PATH", "Machine") 113 | if (!$v.path.EndsWith( $v.sep )) { $v.path += $v.sep } 114 | 115 | $answer = 'keep' 116 | $v.path -split $v.sep | % { 117 | $v.dir = $_.Trim() 118 | 119 | if ($v.dir -eq '') { $s.Empty += 1; return } 120 | 121 | if ($v.dir.EndsWith("\")) { $v.dir = $v.dir -replace '.$'; $s.Trails += 1 } 122 | 123 | $v.edir = [System.Environment]::ExpandEnvironmentVariables($v.dir) 124 | 125 | if ( double ) { return } 126 | if ( invalid) { return } 127 | 128 | if ($Interactive) { $answer = choice } 129 | if (('keep','stop') -contains $answer) { 130 | if ($answer -eq 'stop') { Write-Verbose "User stopped interaction"; $answer = 'keep'; $Interactive = $false } 131 | 132 | if ($Shrink) { $v.dir = shrink $v.dir } 133 | if ($Expand) { $v.dir = expand $v.dir } 134 | $v.npath += $v.dir + $v.sep 135 | } 136 | 137 | if ($answer -eq 'remove' ) { 138 | Write-Verbose "User removed path: $($v.dir)" 139 | $s.user += $v.dir 140 | } 141 | } 142 | 143 | if ($v.npath -eq $v.path ) { check_path_len $v.path; "Path not changed"; return } 144 | 145 | if( $pscmdlet.ShouldProcess("PATH environment variable", "Update") ) 146 | { 147 | if (!(Test-Admin)) { throw "Setting the PATH requires administrative rights" } 148 | 149 | [Environment]::SetEnvironmentVariable("PATH", $v.npath, "Machine") 150 | $backupPath = "PATH_" + (get-date).ToString("yyyy-MM-dd_HHmmss") 151 | [Environment]::SetEnvironmentVariable($backupPath, $v.path, "Machine") 152 | "Old path backed up in the environment variable $backupPath" 153 | 154 | $Env:Path = $v.npath 155 | } else { "Path not changed, only results are shown:" } 156 | 157 | show_stats 158 | $v.npath -split ';' | sort 159 | } 160 | 161 | #Repair-Path -WhatIf 162 | -------------------------------------------------------------------------------- /MM_FileSystem/New-SymLink.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Creates a Symbolic link to a file or directory 4 | 5 | .DESCRIPTION 6 | Creates a Symbolic link to a file or directory as an alternative to mklink.exe. 7 | For details see http://goo.gl/zvFXon 8 | 9 | .NOTES 10 | Author: Boe Prox 11 | Created: 15 Jul 2013 12 | Modified: Miodrag Milic 13 | 14 | .EXAMPLE 15 | New-SymLink -Path "C:\users\admin\downloads" -SymName "C:\users\admin\desktop\downloads" -Directory 16 | 17 | Creates a symbolic link to downloads folder that resides on C:\users\admin\desktop. 18 | 19 | .EXAMPLE 20 | New-SymLink -Path ..\document.txt -SymName "SomeDocument" -File -Force 21 | 22 | Creates or overwrites a symbolic link to "document.txt" in the parent folder to a file "SomeDocument" 23 | under the current directory. 24 | #> 25 | function New-SymLink { 26 | [cmdletbinding( 27 | DefaultParameterSetName = 'Directory', 28 | SupportsShouldProcess=$True 29 | )] 30 | Param ( 31 | # Name of the path that you will reference with a symbolic link. 32 | [parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True)] 33 | [ValidateScript({ if (Test-Path $_) {$true} else { throw "`'$_`' doesn't exist!" } })] 34 | [string]$Path, 35 | 36 | <# 37 | Name of the symbolic link to create. Can be a full path/unc or just the name. 38 | If only a name is given, the symbolic link will be created on the current directory that the 39 | function is being run on. 40 | #> 41 | [string]$SymName, 42 | 43 | # Create a file symbolic link 44 | [parameter(Position=2, ParameterSetName='File')] 45 | [switch]$File, 46 | 47 | # Create a directory symbolic link 48 | [parameter(Position=2, ParameterSetName='Directory')] 49 | [switch]$Directory, 50 | 51 | # Overwrite existing files 52 | [switch]$Force 53 | ) 54 | Begin { 55 | Try { 56 | $null = [mklink.symlink] 57 | } Catch { 58 | Add-Type @" 59 | using System; 60 | using System.Runtime.InteropServices; 61 | 62 | namespace mklink 63 | { 64 | public class symlink 65 | { 66 | [DllImport("kernel32.dll")] 67 | public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); 68 | 69 | [DllImport("kernel32.dll")] 70 | public static extern uint GetLastError(); 71 | } 72 | } 73 | "@ 74 | } 75 | } 76 | Process { 77 | #Assume target Symlink is on current directory if not giving full path or UNC 78 | If ($SymName -notmatch "^(?:[a-z]:\\)|(?:\\\\\w+\\[a-z]\$)") { 79 | $SymName = "{0}\{1}" -f $pwd,$SymName 80 | } 81 | $Flag = @{ 82 | File = 0 83 | Directory = 1 84 | } 85 | If ($PScmdlet.ShouldProcess($Path,'Create Symbolic Link')) { 86 | if ($Force -and (Test-Path $SymName)) { 87 | Write-Verbose "Removing existing destination $($PScmdlet.ParameterSetName)" 88 | rm $SymName -r -force -ea ignore 89 | } 90 | $return = [mklink.symlink]::CreateSymbolicLink($SymName, $Path, $Flag[$PScmdlet.ParameterSetName]) 91 | If ($return) { 92 | $object = New-Object PSObject -Property @{ 93 | SymLink = $SymName 94 | Target = $Path 95 | Type = $PScmdlet.ParameterSetName 96 | } 97 | $object.pstypenames.insert(0,'System.File.SymbolicLink') 98 | $object 99 | } Else { 100 | $err = [mklink.symlink]::GetLastError() 101 | Throw (Get-ErrorMessage $err) 102 | } 103 | } 104 | } 105 | } 106 | 107 | function Get-ErrorMessage { 108 | [OutputType('System.String')] 109 | [CmdletBinding()] 110 | param( 111 | [Parameter(Mandatory = $true)] 112 | [int]$ErrorCode 113 | ) 114 | $signature = @' 115 | [DllImport("kernel32.dll", SetLastError=true)] 116 | public static extern uint FormatMessage( 117 | uint dwFlags, 118 | IntPtr lpSource, 119 | int dwMessageId, 120 | uint dwLanguageId, 121 | ref IntPtr lpBuffer, 122 | uint nSize, 123 | IntPtr Arguments 124 | ); 125 | [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] 126 | public static extern IntPtr LoadLibrary( 127 | string lpFileName 128 | ); 129 | [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] 130 | public static extern bool FreeLibrary( 131 | IntPtr hModule 132 | ); 133 | [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)] 134 | public static extern IntPtr LocalFree( 135 | IntPtr hMem 136 | ); 137 | '@ 138 | 139 | try {Add-Type -MemberDefinition $signature -Name Kernel32 -Namespace PKI} 140 | catch {Write-Warning $Error[0].Exception.Message; return} 141 | $StartBase = 12000 142 | $EndBase = 12176 143 | $ErrorHex = "{0:x8}" -f $ErrorCode 144 | $HighBytes = iex 0x$($ErrorHex.Substring(0,4)) 145 | $LowBytes = iex 0x$($ErrorHex.Substring(4,4)) 146 | $lpMsgBuf = [IntPtr]::Zero 147 | if ($LowBytes -gt $StartBase -and $LowBytes -lt $EndBase) { 148 | $hModule = [PKI.Kernel32]::LoadLibrary("wininet.dll") 149 | $dwChars = [PKI.Kernel32]::FormatMessage(0xb00,$hModule,$LowBytes,0,[ref]$lpMsgBuf,0,[IntPtr]::Zero) 150 | [void][PKI.Kernel32]::FreeLibrary($hModule) 151 | } else {$dwChars = [PKI.Kernel32]::FormatMessage(0x1300,[IntPtr]::Zero,$ErrorCode,0,[ref]$lpMsgBuf,0,[IntPtr]::Zero)} 152 | 153 | if ($dwChars -ne 0) { 154 | ([Runtime.InteropServices.Marshal]::PtrToStringAnsi($lpMsgBuf)).Trim() 155 | [void][PKI.Kernel32]::LocalFree($lpMsgBuf) 156 | } else { 157 | Write-Error -Category ObjectNotFound ` 158 | -ErrorId "ElementNotFoundException" ` 159 | -Message "No error messages are assoicated with error code: 0x$ErrorHex ($ErrorCode). Operation failed." 160 | } 161 | } 162 | 163 | #Export-ModuleMember -Function New-Symlink 164 | Set-Alias ln New-Symlink 165 | -------------------------------------------------------------------------------- /MM_Network/Connect-Mstsc.ps1: -------------------------------------------------------------------------------- 1 | Function Connect-Mstsc { 2 | <# 3 | .SYNOPSIS 4 | Function to connect an RDP session without the password prompt 5 | 6 | .DESCRIPTION 7 | This function provides the functionality to start an RDP session without having to type in the password 8 | 9 | .PARAMETER ComputerName 10 | This can be a single computername or an array of computers to which RDP session will be opened 11 | 12 | .PARAMETER User 13 | The user name that will be used to authenticate 14 | 15 | .PARAMETER Password 16 | The password that will be used to authenticate 17 | 18 | .PARAMETER Credential 19 | The PowerShell credential object that will be used to authenticate against the remote system 20 | 21 | .PARAMETER Admin 22 | Sets the /admin switch on the mstsc command: Connects you to the session for administering a server 23 | 24 | .PARAMETER MultiMon 25 | Sets the /multimon switch on the mstsc command: Configures the Remote Desktop Services session monitor layout to be identical to the current client-side configuration 26 | 27 | .PARAMETER FullScreen 28 | Sets the /f switch on the mstsc command: Starts Remote Desktop in full-screen mode 29 | 30 | .PARAMETER Public 31 | Sets the /public switch on the mstsc command: Runs Remote Desktop in public mode 32 | 33 | .PARAMETER Width 34 | Sets the /w: parameter on the mstsc command: Specifies the width of the Remote Desktop window 35 | 36 | .PARAMETER Height 37 | Sets the /h: parameter on the mstsc command: Specifies the height of the Remote Desktop window 38 | 39 | .NOTES 40 | Name: Connect-Mstsc 41 | Author: Jaap Brasser 42 | DateUpdated: 2016-10-28 43 | Version: 1.2.5 44 | Blog: http://www.jaapbrasser.com 45 | 46 | .LINK 47 | http://www.jaapbrasser.com 48 | 49 | .EXAMPLE 50 | . .\Connect-Mstsc.ps1 51 | 52 | Description 53 | ----------- 54 | This command dot sources the script to ensure the Connect-Mstsc function is available in your current PowerShell session 55 | 56 | .EXAMPLE 57 | Connect-Mstsc -ComputerName server01 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) 58 | 59 | Description 60 | ----------- 61 | A remote desktop session to server01 will be created using the credentials of contoso\jaapbrasser 62 | 63 | .EXAMPLE 64 | Connect-Mstsc server01,server02 contoso\jaapbrasser (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) 65 | 66 | Description 67 | ----------- 68 | Two RDP sessions to server01 and server02 will be created using the credentials of contoso\jaapbrasser 69 | 70 | .EXAMPLE 71 | server01,server02 | Connect-Mstsc -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Width 1280 -Height 720 72 | 73 | Description 74 | ----------- 75 | Two RDP sessions to server01 and server02 will be created using the credentials of contoso\jaapbrasser and both session will be at a resolution of 1280x720. 76 | 77 | .EXAMPLE 78 | server01,server02 | Connect-Mstsc -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Wait 79 | 80 | Description 81 | ----------- 82 | RDP sessions to server01 will be created, once the mstsc process is closed the session next session is opened to server02. Using the credentials of contoso\jaapbrasser and both session will be at a resolution of 1280x720. 83 | 84 | .EXAMPLE 85 | Connect-Mstsc -ComputerName server01:3389 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Admin -MultiMon 86 | 87 | Description 88 | ----------- 89 | A RDP session to server01 at port 3389 will be created using the credentials of contoso\jaapbrasser and the /admin and /multimon switches will be set for mstsc 90 | 91 | .EXAMPLE 92 | Connect-Mstsc -ComputerName server01:3389 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Public 93 | 94 | Description 95 | ----------- 96 | A RDP session to server01 at port 3389 will be created using the credentials of contoso\jaapbrasser and the /public switches will be set for mstsc 97 | 98 | .EXAMPLE 99 | Connect-Mstsc -ComputerName 192.168.1.10 -Credential $Cred 100 | 101 | Description 102 | ----------- 103 | A RDP session to the system at 192.168.1.10 will be created using the credentials stored in the $cred variable. 104 | 105 | .EXAMPLE 106 | Get-AzureVM | Get-AzureEndPoint -Name 'Remote Desktop' | ForEach-Object { Connect-Mstsc -ComputerName ($_.Vip,$_.Port -join ':') -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) } 107 | 108 | Description 109 | ----------- 110 | A RDP session is started for each Azure Virtual Machine with the user contoso\jaapbrasser and password supersecretpw 111 | 112 | .EXAMPLE 113 | PowerShell.exe -Command "& {. .\Connect-Mstsc.ps1; Connect-Mstsc server01 contoso\jaapbrasser (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Admin}" 114 | 115 | Description 116 | ----------- 117 | An remote desktop session to server01 will be created using the credentials of contoso\jaapbrasser connecting to the administrative session, this example can be used when scheduling tasks or for batch files. 118 | #> 119 | [cmdletbinding(SupportsShouldProcess,DefaultParametersetName='UserPassword')] 120 | param ( 121 | [Parameter(Mandatory=$true, 122 | ValueFromPipeline=$true, 123 | ValueFromPipelineByPropertyName=$true, 124 | Position=0)] 125 | [Alias('CN')] 126 | [string[]] $ComputerName, 127 | [Parameter(ParameterSetName='UserPassword',Mandatory=$true,Position=1)] 128 | [Alias('U')] 129 | [string] $User, 130 | [Parameter(ParameterSetName='UserPassword',Mandatory=$true,Position=2)] 131 | [Alias('P')] 132 | [string] $Password, 133 | [Parameter(ParameterSetName='Credential',Mandatory=$true,Position=1)] 134 | [Alias('C')] 135 | [PSCredential] $Credential, 136 | [Alias('A')] 137 | [switch] $Admin, 138 | [Alias('MM')] 139 | [switch] $MultiMon, 140 | [Alias('F')] 141 | [switch] $FullScreen, 142 | [Alias('Pu')] 143 | [switch] $Public, 144 | [Alias('W')] 145 | [int] $Width, 146 | [Alias('H')] 147 | [int] $Height, 148 | [Alias('WT')] 149 | [switch] $Wait 150 | ) 151 | 152 | begin { 153 | [string]$MstscArguments = '' 154 | switch ($true) { 155 | {$Admin} {$MstscArguments += '/admin '} 156 | {$MultiMon} {$MstscArguments += '/multimon '} 157 | {$FullScreen} {$MstscArguments += '/f '} 158 | {$Public} {$MstscArguments += '/public '} 159 | {$Width} {$MstscArguments += "/w:$Width "} 160 | {$Height} {$MstscArguments += "/h:$Height "} 161 | } 162 | 163 | if ($Credential) { 164 | $User = $Credential.UserName 165 | $Password = $Credential.GetNetworkCredential().Password 166 | } 167 | } 168 | process { 169 | foreach ($Computer in $ComputerName) { 170 | $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo 171 | $Process = New-Object System.Diagnostics.Process 172 | 173 | # Remove the port number for CmdKey otherwise credentials are not entered correctly 174 | if ($Computer.Contains(':')) { 175 | $ComputerCmdkey = ($Computer -split ':')[0] 176 | } else { 177 | $ComputerCmdkey = $Computer 178 | } 179 | 180 | $ProcessInfo.FileName = "$($env:SystemRoot)\system32\cmdkey.exe" 181 | $ProcessInfo.Arguments = "/generic:TERMSRV/$ComputerCmdkey /user:$User /pass:$($Password)" 182 | $ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden 183 | $Process.StartInfo = $ProcessInfo 184 | if ($PSCmdlet.ShouldProcess($ComputerCmdkey,'Adding credentials to store')) { 185 | [void]$Process.Start() 186 | } 187 | 188 | $ProcessInfo.FileName = "$($env:SystemRoot)\system32\mstsc.exe" 189 | $ProcessInfo.Arguments = "$MstscArguments /v $Computer" 190 | $ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal 191 | $Process.StartInfo = $ProcessInfo 192 | if ($PSCmdlet.ShouldProcess($Computer,'Connecting mstsc')) { 193 | [void]$Process.Start() 194 | if ($Wait) { 195 | $null = $Process.WaitForExit() 196 | } 197 | } 198 | } 199 | } 200 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /MM_Admin/New-ProxyCommand.ps1: -------------------------------------------------------------------------------- 1 | Function New-ProxyCommand { 2 | <# 3 | .SYNOPSIS 4 | Generate the sourcecode for a ProxyCommand to call a base Cmdlet adding or removing functionality (parameters). 5 | 6 | .DESCRIPTION 7 | Generate the sourcecode for a ProxyCommand to call a base Cmdlet adding or removing functionality (parameters). 8 | 9 | This command generates a command which calls another command (a ProxyCommand). 10 | In doing so, it can add new parameters or remove existing parameters from the original command. 11 | Even you can add functionality or change the behavior of the command. 12 | If you ADD a parameter, you'll have 13 | to implement the semantics of that parameter in the code that gets generated. 14 | 15 | There are bits of background knowledge you need for proxy functions. 16 | 17 | 1. Command Precedence (See: Get-Help about_Command_Precedence) 18 | If you do not specify a path, Windows PowerShell uses the following 19 | precedence order when it runs commands: 20 | 1. Alias 21 | 2. Function 22 | 3. Cmdlet 23 | 4. Native Windows commands 24 | Aliases beat Functions, Functions beat Cmdlets. Cmdlets beat external scripts and programs. 25 | A function named "Get-ChildItem" will be called instead of a cmdlet named "Get-ChildItem" – meaning a 26 | function can replace a cmdlet simply by giving it the same name. That is the starting point for a Proxy function. 27 | 28 | 2. Steppable Pipeline 29 | A pipeline (Functions and Cmdlets) can have a 3 internal Named Scriptblocks with the keywords Begin{}, Process{} and End{}. 30 | The Begin{} block runs once and initialisize the command (it is recomended that the Begin{} block should not do any output!) 31 | The Begin{} block is intended to open needed rescources (databases), or to initialisize Variables inner nested functions. 32 | The Process{} block runs for each item passed via the pipeline (it is best to do command output here) 33 | The End{} block runs after the last item has passed through Process[} block (the End{} block can do output (eg. on a sort process)) 34 | The End{} block schould do the cleanup work to close rescources (databases) or to do tidy up work. 35 | Given a script block that contains a single pipeline, the GetSteppablePipeline() 36 | method returns a SteppablePipeline object that gives you control over the Begin, Process, and End 37 | stages of the pipeline. 38 | 3. Argument Splatting. 39 | Given a hashtable of names and values, PowerShell lets you pass the entire 40 | hashtable to a command. If you use the @ symbol to identify the hashtable variable name (rather 41 | than the $ symbol), PowerShell then treats each element of the hashtable as though it were a 42 | parameter to the command. 43 | 44 | 4. .NET classes to create a ProxyCommand 45 | 46 | 1. We expose the metadata. You can do a New-Object on System.Management.Automation.CommandMetaData passing it cmdletInfo and get it's metadata. 47 | Try this to create Metadate from the cmdlet "Get-Process": 48 | PS> New-Object System.Management.Automation.CommandMetaData (Get-Command Get-Process) 49 | 50 | 2. We make the metadata programmable. You can add/remove parameters, change the parameters, change the name, etc. 51 | 52 | 3. We use metadata to emit a script Cmdlet. 53 | PS> $metaData = New-Object System.Management.Automation.CommandMetaData (Get-Command Get-Process) 54 | PS> [System.Management.Automation.ProxyCommand]::create($MetaData) 55 | 56 | 5. A command can be invoked as moduleName\CommandName. 57 | If you have created a ProxyCommand to shadow a Cmdlet, the original Cmdlet can be called by use of the Cmdlet-Module path! 58 | If you have done a ProxyCommand to shadow the Cmdlet "Get-ChildItem", you can call the original Cmdlet like so: 59 | Microsoft.PowerShell.Management\Get-ChildItem 60 | 61 | 62 | See Link: Extending and/or Modifing Commands with Proxies 63 | http://blogs.msdn.com/b/powershell/archive/2009/01/04/extending-and-or-modifing-commands-with-proxies.aspx 64 | 65 | See Link: Customizing PowerShell, Proxy functions and a better Select-String 66 | http://jamesone111.wordpress.com/2012/02/04/customizing-powershell-proxy-functions-and-a-better-select-string/ 67 | 68 | 69 | .PARAMETER Name 70 | Name of the Cmdlet to proxy. 71 | You can Proxy cmdlets or functions 72 | The name of the command can be an module path like: Microsoft.Powershell.Management\Get-ChildItem 73 | 74 | .PARAMETER NewName 75 | Provide a new command name if you like to create a new function that is similar to an existing command. 76 | The command from the -Name parameter is taken as template for the command with this new name. 77 | This new name is used as function name and the old name from the -Name parameter is replaced by this new name in the sourcecode text. 78 | 79 | .PARAMETER Path 80 | Specifies the path to output the resulting sourcecode as a textfile. 81 | Note: If this function runs inside the PowerShell ISE, the resulting sourcecode is inserted in a new Editor-Tab by default. 82 | 83 | .PARAMETER CommandType 84 | Type of Command we are proxying. In general you dont' need to specify this but 85 | it becomes necessary if there is both a cmdlet and a function with the same 86 | name 87 | Valid arguments are 'Cmdlet' or 'function' the default is 'cmdlet' 88 | 89 | .PARAMETER AddParameter 90 | List of Parameters as type of 'System.Management.Automation.ParameterMetadata' you would like to add 91 | You can even simpy provide an list of Names as type of Strings 92 | NOTE: 93 | you have to edit the resultant code to implement the semantics of these parameters. 94 | ALSO - you need to remove them from $PSBoundParameters on a call of the origin command! 95 | 96 | You can use the New-Parameter Function to create a new ParameterMetadata Object 97 | 98 | .PARAMETER RemoveParameter 99 | List of Parameters as type of 'System.String' you would like to remove from the origin command. 100 | 101 | .PARAMETER ExcludeHelp 102 | Provide this Parameter if you do not want to add the helptext (comment based help) of the original command 103 | 104 | .PARAMETER NoIseInsert 105 | Provide this Parameter if you do not want to insert the sourcecode produced by this function into the PowerShell ISE. 106 | If this function runs inside the PowerShell ISE, the resulting sourcecode is inserted in a new Editor-Tab by default. 107 | 108 | .EXAMPLE 109 | New-ProxyCommand Get-Alias -AddParameter 'SortBy' 110 | 111 | Create an proxy function from the 'Get-Alias' cmdlet and add a new Parameter with Name 'SortBy' 112 | 113 | .EXAMPLE 114 | New-ProxyCommand -Name Get-Alias -AddParameter 'SortBy' -Path E:\temp\test\Get-MyAliasISE.ps1 115 | 116 | Create an proxy function from the 'Get-Alias' cmdlet, add a new Parameter with Name 'SortBy' and 117 | save the resulting sourcecode into the file 'E:\temp\test\Get-MyAliasISE.ps1' 118 | 119 | .EXAMPLE 120 | New-ProxyCommand -Name Get-Alias -AddParameter 'SortBy' -Path E:\temp\test\Get-MyAlias.ps1 -NoIseInsert 121 | 122 | Create an proxy function from the 'Get-Alias' cmdlet, add a new Parameter with Name 'SortBy', 123 | save the resulting sourcecode into the file 'E:\temp\test\Get-MyAliasISE.ps1', and prevent outpot to the PowerShell ISE 124 | 125 | .OUTPUTS 126 | System.String 127 | The sourcecode of the command to proxy as System.String 128 | 129 | .NOTES 130 | 131 | #Requires PowerShell -Version 2.0 132 | 133 | NAME: New-ProxyCommand 134 | AUTHOR: NTDEV\jsnover 135 | ToDo: Need to modify script to emit template help for the proxy command. 136 | Probably should add a -AsFunction switch 137 | LASTEDIT: 1/4/2009 8:53:35 AM 138 | See Link: Extending and/or Modifing Commands with Proxies 139 | http://blogs.msdn.com/b/powershell/archive/2009/01/04/extending-and-or-modifing-commands-with-proxies.aspx 140 | 141 | Edited by: Peter Kriegel 142 | Initial release: 18.June.2014 143 | Version: 2.0.0 144 | LASTEDIT: 22.June.2014 145 | 146 | History: 147 | Switcht from use of here Strings to Stringbuilder 148 | removed inner function 149 | added parameter -Newname and functionality to use a new command name 150 | added ability to extract the help from origin command 151 | added parameter ExcludeHelp and functionality 152 | 153 | #> 154 | [CmdletBinding( 155 | SupportsShouldProcess=$False, 156 | SupportsTransactions=$False, 157 | ConfirmImpact="None", 158 | DefaultParameterSetName="")] 159 | param( 160 | [Parameter(Position=0, Mandatory=$True)] 161 | [String]$Name, 162 | 163 | [String]$NewName, 164 | 165 | [String]$Path, 166 | 167 | [Alias("Type")] 168 | [ValidateSet('Cmdlet','Function')] 169 | [System.Management.Automation.CommandTypes]$CommandType='Cmdlet', 170 | 171 | [System.Management.Automation.ParameterMetadata[]]$AddParameter, 172 | 173 | [String[]]$RemoveParameter, 174 | 175 | [Switch]$ExcludeHelp, 176 | 177 | [Switch]$NoIseInsert 178 | ) 179 | 180 | 181 | # create a StringBuilder to glue the sourcecode 182 | # with StringBuilder we have better control over newline 183 | $stringBuilder = New-Object System.Text.StringBuilder 184 | 185 | # The name of the command can be an module path like: Microsoft.Powershell.Management\Get-ChildItem 186 | # so we split out only the leaf namen 187 | $OriginCommandName = Split-Path $Name -Leaf 188 | 189 | # If the original command is modified, or the original hcommant help is included, 190 | # we remove all links to the original command 191 | # create a flag for this 192 | $NoForwardHelp = $False 193 | If((-not [String]::IsNullOrEmpty($NewName)) -or 194 | (-not [String]::IsNullOrEmpty($RemoveParameter)) -or 195 | ($AddParameter.count -gt 0) -or 196 | (-Not $ExcludeHelp.IsPresent) 197 | ) 198 | { 199 | $NoForwardHelp = $True 200 | } 201 | 202 | 203 | # try to get the metadata from the command 204 | Try { 205 | $Cmd = Get-Command -Name $Name -CommandType $CommandType -ErrorAction stop 206 | } catch { 207 | Throw $_ 208 | # exit function 209 | Return 210 | } 211 | 212 | # if the command exist more then once throw an Error and exit function 213 | if (@($cmd).Count -ne 1) { 214 | Throw "Command exist more then once!`nAmbiguous reference [$Name : $CommandType]`n$($Cmd | Out-String)" 215 | # exit function 216 | Return 217 | } 218 | 219 | ## If a function already exists with this name (perhaps it's already been 220 | ## wrapped,) output a warning message 221 | if(Test-Path function:\$OriginCommandName) { 222 | Write-Warning "A Function with the Name: '$OriginCommandName' already exist!" 223 | } 224 | 225 | # get metadata from the command 226 | $MetaData = New-Object System.Management.Automation.CommandMetaData $cmd 227 | 228 | 229 | if ($RemoveParameter) 230 | { 231 | foreach ($ParameterName in @($RemoveParameter)) 232 | { 233 | # Remove Parameters by Name from the command metadata 234 | [Void]$MetaData.Parameters.Remove($ParameterName) 235 | } 236 | } 237 | 238 | 239 | # Add comment to the Output Text 240 | If([String]::IsNullOrEmpty($NewName)) { 241 | [void]$stringBuilder.AppendLine("# Begin of ProxyCommand for command: $OriginCommandName") 242 | } 243 | 244 | # Add the head of the function 245 | If([String]::IsNullOrEmpty($NewName)) { 246 | [void]$stringBuilder.AppendLine("Function $OriginCommandName {") 247 | } else { 248 | [void]$stringBuilder.AppendLine("Function $NewName {") 249 | } 250 | # Add Comment based help 251 | If(-Not $ExcludeHelp.IsPresent) { 252 | # create comment based helptext and reformat it line by line 253 | $IsCommendHelpKeyword = $False 254 | ("<#$([System.Management.Automation.ProxyCommand]::GetHelpComments((Get-Help $Name)))#>") -split "`n" | ForEach-Object { 255 | 256 | $Line = ([String]$_).Trim() 257 | If(-not [String]::IsNullOrEmpty($NewName)) { 258 | $Line = $Line -replace 'Get-ChildItem','Get-Popel' 259 | } 260 | 261 | If($Line -eq '') { 262 | $EmptyLineCounter++ 263 | } Else { 264 | $EmptyLineCounter = 0 265 | } 266 | 267 | If($Line -like '.*' -or ($Line -eq '<#') -or ($Line -eq '#>')){ 268 | # return line without a tab in front 269 | # if it is a comment based help keyword or a comment indicator 270 | $IsCommendHelpKeyword = $True 271 | [void]$stringBuilder.AppendLine($Line) 272 | }Else{ 273 | # return line with a tab in front 274 | 275 | # do not return the empty line if they folow directly after a comment based help keyword 276 | If(-not ($IsCommendHelpKeyword -and ($Line -eq ''))) { 277 | # return line with a tab in front 278 | # if it is not a comment based help keyword 279 | If($EmptyLineCounter -lt 2) { # supress more than 1 empty lines 280 | [void]$stringBuilder.AppendLine("`t$Line") 281 | } 282 | } 283 | $IsCommendHelpKeyword = $False 284 | } 285 | } 286 | # append comment based help text 287 | [void]$stringBuilder.AppendLine($CommentHelpText) 288 | } 289 | 290 | If ($AddParameter) { 291 | [void]$stringBuilder.AppendLine('<#') 292 | [void]$stringBuilder.AppendLine('You are responsible for implementing the logic for added parameters. These ') 293 | [void]$stringBuilder.AppendLine('parameters are bound to $PSBoundParameters so if you pass them on the the ') 294 | [void]$stringBuilder.AppendLine('command you are proxying, it will almost certainly cause an error. This logic') 295 | [void]$stringBuilder.AppendLine('should be added to your BEGIN statement to remove any specified parameters ') 296 | [void]$stringBuilder.AppendLine('from $PSBoundParameters.') 297 | [void]$stringBuilder.AppendLine('') 298 | [void]$stringBuilder.AppendLine('In general, the way you are going to implement additional parameters is by') 299 | [void]$stringBuilder.AppendLine('modifying the way you generate the $scriptCmd variable. Here is an example') 300 | [void]$stringBuilder.AppendLine('of how you would add a -SORTBY parameter to a cmdlet:') 301 | [void]$stringBuilder.AppendLine('') 302 | [void]$stringBuilder.AppendLine(' if ($SortBy)') 303 | [void]$stringBuilder.AppendLine(' {') 304 | [void]$stringBuilder.AppendLine(' [Void]$PSBoundParameters.Remove("SortBy")') 305 | [void]$stringBuilder.AppendLine(' $scriptCmd = {& $wrappedCmd @PSBoundParameters |Sort-Object -Property $SortBy}') 306 | [void]$stringBuilder.AppendLine(' }else') 307 | [void]$stringBuilder.AppendLine(' {') 308 | [void]$stringBuilder.AppendLine(' $scriptCmd = {& $wrappedCmd @PSBoundParameters }') 309 | [void]$stringBuilder.AppendLine(' }') 310 | [void]$stringBuilder.AppendLine('') 311 | [void]$stringBuilder.AppendLine('################################################################################ ') 312 | [void]$stringBuilder.AppendLine('New ATTRIBUTES:') 313 | 314 | foreach ($ParameterMetadata in @($AddParameter)) 315 | { 316 | [Void]$MetaData.Parameters.Add($ParameterMetadata.Name, $ParameterMetadata) 317 | 318 | [void]$stringBuilder.AppendLine(" if (`$$($ParameterMetadata.Name))") 319 | [void]$stringBuilder.AppendLine(" {") 320 | [void]$stringBuilder.AppendLine(" [Void]`$PSBoundParameters.Remove($($ParameterMetadata.Name))") 321 | [void]$stringBuilder.AppendLine(" }") 322 | 323 | } 324 | 325 | [void]$stringBuilder.AppendLine('################################################################################') 326 | [void]$stringBuilder.AppendLine('#>') 327 | 328 | } # end If($AddParameter) 329 | 330 | [void]$stringBuilder.AppendLine() 331 | 332 | # create the command sourcecode from metadata 333 | $CommandText = [System.Management.Automation.ProxyCommand]::create($MetaData) 334 | 335 | If($NoForwardHelp) { 336 | # Regex to remove the help forwarding to the origin command 337 | $regex = New-Object Text.RegularExpressions.Regex "\<\#.*\.ForwardHelpTargetName.*\.ForwardHelpCategory.*\#\>", ('singleline','multiline','IgnoreCase') 338 | $CommandText = $regex.Replace($CommandText,'') 339 | # Regex to replace the HelpUri 340 | $CommandText = $CommandText -Replace ", HelpUri='http://.*?'", '' 341 | } 342 | # Add a Tab in front of each Line 343 | $CommandText = $CommandText -split "`n" | ForEach-Object { "`t$_`n" } 344 | [void]$stringBuilder.AppendLine($CommandText) 345 | #[void]$stringBuilder.AppendLine(([System.Management.Automation.ProxyCommand]::create($MetaData))) 346 | If([String]::IsNullOrEmpty($NewName)) { 347 | [void]$stringBuilder.AppendLine("} # End ProxyFunction for command: $OriginCommandName") 348 | }Else{ 349 | [void]$stringBuilder.AppendLine("} # End of function: $NewName") 350 | } 351 | 352 | $OutputText = $stringBuilder.ToString() 353 | 354 | # if we are in the PowerShell ISE we create a new File-Tab and 355 | # insert the sourcecode of the ProxyCommand in the Editor of the new created File-Tab 356 | If($psise -and (-not $NoIseInsert.IsPresent)) { 357 | 358 | 359 | # Create File to open in ISE if Filepath was given 360 | If(-not [String]::IsNullOrEmpty($Path)) { 361 | '' | Out-File -FilePath $Path 362 | # create a new File Tab in the ISE 363 | $File = $psise.PowerShellTabs.SelectedPowerShellTab.Files.Add($Path) 364 | } Else { 365 | # No filepath was given, open File without filepath 366 | # create a new File Tab in the ISE 367 | $File = $psise.PowerShellTabs.SelectedPowerShellTab.Files.Add() 368 | } 369 | 370 | # call Internal Function to set the Text of the ISE Editor Text in new File Tab 371 | $File.Editor.Text = $OutputText 372 | # scroll to first char 373 | $File.Editor.Select(1,1,1,1) 374 | } 375 | 376 | If(-not [String]::IsNullOrEmpty($Path)) { 377 | If($psise -and (-not $NoIseInsert.IsPresent)) { 378 | $File.SaveAs($Path) 379 | } Else { 380 | $OutputText | Out-File -FilePath $Path 381 | } 382 | } 383 | 384 | # allways return the Text of the ProxyCommand 385 | $OutputText 386 | 387 | 388 | }# end function internal 389 | 390 | New-ProxyCommand -Name 'Get-ChildItem' -CommandType Cmdlet -NewName Get-Popel --------------------------------------------------------------------------------