Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save romero126/230219c0b9eee1995d2b5079ff6bc3ed to your computer and use it in GitHub Desktop.
Save romero126/230219c0b9eee1995d2b5079ff6bc3ed to your computer and use it in GitHub Desktop.
Command LookupAccelerator (PreCommandLookupAction)

Description

Enables CommandLookup to be more context aware.

image image

Example

. PreCommandLookupAction.ps1
. PreCommandLookup_InvokeMachineAccelerator.ps1

# Pattern Match output based on computer name.
# Both localhost and a regex pattern is used so it can be contexually aware.
# Note: Pattern matching can also add move value as you can
#       sort on machinetype and provide even greater contextual actions
PHX01FOREST01DC001 Service -Name "TermService"
localhost Service -Name "TermService"
if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage') {
throw "This script requires `$ExecutionContext.SessionState.LanguageMode to be FullLanguageMode but got '$($ExecutionContext.SessionState.LanguageMode )'"
}
$global:PSPreCommandLookupAction += @{
Name = "ApplockerSignedScriptRestrictions"
Description = "This will bypass Applocker's Signed Script Restrictions by loading the script directly into memory as if it was dot sourced"
Filter = { $command -like "*.ps1" }
Priority = -1
StopSearch = $true
LookupAction = {
# Check if command already exists as a command
$_commandExists = Microsoft.PowerShell.Core\Get-Command -Name $command -ErrorAction Ignore
#if ($_commandExists -or $_commandExists.ScriptBlock -ne $null) { return }
if ($_commandExists) { return }
# Check if file exists
$_commandFileExists = Get-Item -Path $command -ErrorAction SilentlyContinue
if ($_commandFileExists)
{
$_errors = $null
$_tokens = $null
$_ast = [System.Management.Automation.Language.Parser]::ParseFile($_commandFileExists.FullName, [ref] $_tokens, [ref] $_errors)
if ($_errors) {
$eventArgs.CommandScriptBlock = { throw $_errors }
return
}
$_scriptblock = $_ast.GetScriptBlock()
$eventArgs.CommandScriptBlock = $_scriptblock
}
}
}
function Invoke-MachineAccelerator {
param(
[Parameter(Position = 0)]
[System.Management.Automation.ValidateSet(
"ChildItem", "Service", "Process", "NetAdapter", "ipconfig"
)]
[System.Management.Automation.Alias('Type')]
[System.String] $psCommandType
)
dynamicparam
{
# Define common parameters
#$commonParameters = [System.Management.Automation.Internal.CommonParameters].DeclaredProperties.Name
# Define more stateful lookups to be ran
#$command = $MyInvocation.MyCommand.Name -replace "LookupHandlerReplacementFor<<(.*)>>", '$1'
#$machineType = $command -replace "^([a-zA-Z]{2,3}[0-9]+)([a-zA-Z]{3,4}[\d]+)([a-zA-Z]{2})([\d]+)$", '$3'
# Define which parameters to apply to the command accelerator
$this_lookupAction, $this_lookupActionParameterBinding = switch ($psCommandType) {
"ChildItem" { "Get-ChildItem", $null }
"Service" { "Get-Service", $null }
"Process" { "Get-Process", $null }
"NetAdapter" { "Get-NetAdapter", $null }
"ipconfig" {
{
[CmdletBinding()]
param(
$args
)
ipconfig $args
}, $null
}
{ $psCommandType -eq $null } {
$sb = {
Get-ComputerInfo
}
$sb, $null
}
default {
$null
}
}
if ($this_lookupAction -is [ScriptBlock]) {
# Use ScriptBlock
$this_command = $this_lookupAction
$this_commandName = "{ ScriptBlock }"
$this_commandParameters = [scriptblock].GetProperties('Instance, NonPublic').Where({$_.Name -like 'RuntimeDefinedParameters'}).GetValue($this_command)
} else {
# Command Lookup
$this_command = Microsoft.PowerShell.Core\Get-Command -Name $this_lookupAction
$this_commandName = "{0}\{1}" -f $this_command.ModuleName, $this_command.Name
$this_commandParameters = $this_command.Parameters
}
# Lookup the full command here
# Export the Command Parameter Dictionary
# Note:
# Parameters with a Position Attribute will not show up in CommandCompletion however they still exist
#
$_paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
foreach ($_param in $this_commandParameters.GetEnumerator())
{
if ($null -ne $this_lookupActionParameterBinding -and $_param.Value.Name -eq $this_lookupActionParameterBinding) { continue }
if ($_param.Value.Name -in [System.Management.Automation.Cmdlet]::CommonParameters ) { continue }
$_attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]$_param.Value.Attributes
$_parameter = New-Object System.Management.Automation.RuntimeDefinedParameter($_param.Value.Name, $_param.Value.ParameterType, $_attributeCollection)
$_paramDictionary.Add($_param.Value.Name, $_parameter)
}
return $_paramDictionary
}
begin {
if ($MyInvocation.MyCommand.Name -eq "Invoke-GetCentralAdminMachineAccelerator") {
throw "Invoke-GetCentralAdminMachineAccelerator is not meant to be called directly. It is a proxy command."
}
# Bind the mapped parameter to the command to be ran
$null = $PSBoundParameters.Remove("psCommandType")
$commandName = $MyInvocation.MyCommand.Name -replace "LookupHandlerReplacementFor<<(.*)>>", '$1'
if ($null -ne $this_lookupActionParameterBinding) {
$PSBoundParameters[$this_lookupActionParameterBinding] = $commandName
}
# Show the actual command being ran.
$_stringBoundParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object { "-$($_.Key) $($_.Value)" }
$_stringBoundParameters = $_stringBoundParameters -join " "
Write-Host "Invoking: $this_commandName $_stringBoundParameters" -ForegroundColor Cyan
if (-not $this_lookupActionParameterBinding -and $commandName -ne 'localhost') {
Invoke-Command -ScriptBlock $this_command -ComputerName $commandName -ArgumentList $PSBoundParameters
return
}
. $this_command @PSBoundParameters
}
process {
}
end {
Write-Host 'Completed'
}
}
# Lets create an accelerator
$global:PSPreCommandLookupAction += @{
Name = "Pattern Match on machine"
Description = "Matches pattern on MachineName"
# Filter is <Datacenter><Forest><MachineType><MachineNumber>
# Example PHX01FOREST01DC001
Filter = { $command -match "^([a-zA-Z]+[\d]+)([a-zA-Z]+[\d]+)([a-zA-Z]+)([\d]+)$|localhost"}
Priority = -1
StopSearch = $true
ScriptBlock = (Get-Command "Invoke-MachineAccelerator").ScriptBlock
}
$global:PSPreCommandLookupAction = @()
# We dont patch unless it is possible.
if ($ExecutionContext.SessionState.LanguageMode -eq 'FullLanguage') {
Write-Verbose "Patching PreCommandLookupAction"
# We have custom event handlers that handles actions
# In this demo we only look at PreCommandLookupAction
#
# CommandNotFoundAction
# PostCommandLookupAction
# PreCommandLookupAction
# LocationChangedAction
# Patch only if we need to
if ($ExecutionContext.InvokeCommand.PreCommandLookupAction) {
Write-Verbose "PreCommandLookupAction is already patched"
}
else {
$ExecutionContext.InvokeCommand.PreCommandLookupAction = {
param(
[Parameter()]
$command,
$eventArgs
)
foreach ($action in ($global:PSPreCommandLookupAction | Sort-Object -Property Priority -Descending))
{
try {
$_commandMatch = . $action.Filter.GetNewClosure()
if (-not $_commandMatch) { continue }
if ($action.ScriptBlock) {
$eventArgs.CommandScriptBlock = $action.ScriptBlock.GetNewClosure()
$eventArgs.StopSearch = $action.StopSearch
return
} elseif ($action.LookupAction) {
# Lookup action Invokes a script prior to the script being found
# but does not interrupt the search after. So we can use this to capture inputs here
# Note:
#
# We can interrupt the search after the script is called by calling
# $eventArgs.StopSearch = $true
#
. $action.LookupAction.GetNewClosure()
}
}
catch {
Write-Debug "Action Failed $($action.Name)"
Write-Debug $_
return
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment