Pass parameter to powershell encoded command - powershell

I have a script which has quite a lot of lines.
I can easily paste this script in a scriptblock parameter without having to edit it (e.g. put backslashes in front of quotes in the script). I can then encode the script so it can be passed to powershell as en encoded parameter:
$myscript = {
#paste of simplified script
$calc = 6 + 9
echo $calc
}
# Convert script to a string
$command = $carvingScript.ToString()
# Convert string to base64 encoded command
$bytes = [System.Text.Encoding]::Unicode.GetBytes( $command )
$encodedCommand = [Convert]::ToBase64String( $bytes )
I would like to be able to pass one parameter in the script that gets base64 converted. Like this:
$parameter = 9
$myscript = {
$calc = 6 + $parameter
echo $calc
}
Any ideas how to tackle this? I know scriptblock can contain arguments, but in order to parse the argument the whole script needs to be parsed, not just the one parameter

The direct answer to how to add variables to a script block is this:
$parameter = 9
$myscript = #'
$calc = 6 + {0}
echo $calc
'# -f $parameter
$scriptblock = [scriptblock]::Create($myscript)
Basically build it as a string and use the create method from [scriptblock] to convert.
But you can skip creating the scriptblock since you will just convert it back to a string directly afterwards.

It's an old post, but I found this article which worked for me, so I want to share it with you dear community :)
You can use param block with mandatory parameters inside your script block:
$myscript = {
param
(
[Parameter(Mandatory)]
[decimal]
$First,
[Parameter(Mandatory)]
[decimal]
$Second
)
[decimal]($First + $Second)
}
$bytes = [System.Text.Encoding]::Unicode.GetBytes($myscript)
$encodedCommand = [Convert]::ToBase64String($bytes)
$encodedCommand | Set-Content 'C:\temp\encodedCommand.txt' -Encoding UTF8
Then pass parameters by pipeline between two powershell.exe calls:
powershell.exe -noprofile -command "3.3, 2.7" | powershell.exe -encodedcommand DQAKACAAIAAgACAAcABhAHIAYQBtAA0ACgAgACAAIAAgACgADQAKACAAIAAgACAAIAAgACAAIABbAFAAYQByAGEAbQBlAHQAZQByACgATQBhAG4AZABhAHQAbwByAHkAKQBdAA0ACgAgACAAIAAgACAAIAAgACAAWwBkAGUAYwBpAG0AYQBsAF0ADQAKACAAIAAgACAAIAAgACAAIAAkAEYAaQByAHMAdAAsAA0ACgANAAoAIAAgACAAIAAgACAAIAAgAFsAUABhAHIAYQBtAGUAdABlAHIAKABNAGEAbgBkAGEAdABvAHIAeQApAF0ADQAKACAAIAAgACAAIAAgACAAIABbAGQAZQBjAGkAbQBhAGwAXQANAAoAIAAgACAAIAAgACAAIAAgACQAUwBlAGMAbwBuAGQADQAKACAAIAAgACAAKQANAAoAIAAgACAAIABbAGQAZQBjAGkAbQBhAGwAXQAoACQARgBpAHIAcwB0ACAAKwAgACQAUwBlAGMAbwBuAGQAKQANAAoA
This is using Powershell interactive input mode which is visible in the overall output, so be aware if you pass any passwords or secrets:
cmdlet at command pipeline position 1
Supply values for the following parameters:
First: 3.3
Second: 2.7
6.0
If you ever try to have list (array) parameter and pass list of values to the encoded command then you need to remember that last array element must be an empty string - this is how you trick interactive input mode into setting the list parameter.
You also need to remember to do not mark list parameter as ValueFromPipeline otherwise, it will not consume values properly.
$command = {
param
(
[Parameter(Mandatory)]
[string[]]
$MyList
)
$MyList | ForEach-Object { Write-Host $_ }
}
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
"powershell.exe -noprofile -command `"'test1', 'test2', 'test3', ''`" | powershell.exe -encodedcommand $encodedCommand" | Set-Content 'C:\temp\test.txt' -Encoding UTF8
PS C:\temp> powershell.exe -noprofile -command "'test1', 'test2', 'test3', ''" | powershell.exe -encodedcommand DQAKACAAIAAgACAAcABhAHIAYQBtAA0ACgAgACAAIAAgACgADQAKACAAIAAgACAAIAAgACAAIABbAFAAYQByAGEAbQBlAHQAZQByACgATQBhAG4AZABhAHQAbwByAHkAKQBdAA0ACgAgACAAIAAgACAAIAAgACAAWwBzAHQAcgBpAG4AZwBbAF0AXQANAAoAIAAgACAAIAAgACAAIAAgACQATQB5AEwAaQBzAHQADQAKACAAIAAgACAAKQANAAoAIAAgACAAIAAkAE0AeQBMAGkAcwB0ACAAfAAgAEYAbwByAEUAYQBjAGgALQBPAGIAagBlAGMAdAAgAHsAIABXAHIAaQB0AGUALQBIAG8AcwB0ACAAJABfACAAfQANAAoA
cmdlet at command pipeline position 1
Supply values for the following parameters:
MyList[0]: test1
MyList[1]: test2
MyList[2]: test3
MyList[3]:
test1
test2
test3
I hope it'll help someone in the future. Peace!

Related

give value into powershell script

I want to use a string inside a PowerShell script. It should be handed over like a variable when executing the script like this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -c C:\temp\myscript.ps1 "string"
That is working well with just one word. But my string looks like this and should be handed over:
**<UserInputs><UserInput Question="Gruppenname" Answer="<Values Count="1"><Value DisplayName="Humanresources" Id="af05c5d3-2312-c897-8439-08979d4d0a49" /></Values>" Type="System.SupportingItem.PortalControl.InstancePicker" /><UserInput Question="Ausgabe" Answer="Namen" Type="richtext" /></UserInputs>**
This string contains some quotation marks and I have problems injecting it into my script.
inside the script I have this:
$mystring = $Null if($args[0] -ne $Null) { $mystring = $args[0] } $result = $mystring | Select-String -Pattern "DisplayName="(.*?)"" $result= $result.Matches.Groups[1] $group = $result.value Write-Output "$group" | Out-file C:\temp\group.txt
PowerShell scripts can take parameters, which become variables that are just available to the rest of the script. Use this at the top of your script to setup three variables, one boolean (true false) type, one string type and one integer type.
#stack.ps1
param(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$MyString="MyDefaultValue",
[int]$MyIntInput,
[bool]$MyTrueFalseInput
)
"Script running!"
"Values recieved!"
"MyString = $MyString"
"MyIntInput = $MyIntInput"
"MyTrueFalse = $MyTrueFalseInput"
Then to pass in values
C:\temp> .\stack.ps1 -MyString Ham -MyIntInput 75 -MyTrueFalseInput $true
Script running!
Values recieved!
MyString = Ham
MyIntInput = 75
MyTrueFalse = True
How to work with odd or complex strings
Now, to pass in a complex string that has quotes, use this syntax:
#using single quotes as a delimiter, ignoring the doubles inside.
$myWeirdString = 'ThisIs"SomeString"WhichHasQuotes"WhichIsWeird'
## extra case, if it's really odd and has both quote types
$myWeirdString = #"
'ThisIs"SomeString"WhichHasQuotes"WhichIsWeird'
"#

Redirect standard input (read-host) to a Powershell script

Here's a sample powershell script:
$in = read-host -prompt "input"
write-host $in
Here's a sample 'test.txt' file:
hello
And we want to pass piped input to it from powershell. Here's some I have tried:
.\test.ps1 < test.txt
.\test.ps1 < .\test.txt
.\test.ps1 | test.txt
.\test.ps1 | .\test.txt
test.txt | .\test.ps1
.\test.txt | .\test.ps1
get-content .\test.txt | .\test.ps1
even just trying to echo input doesn't work either:
echo hi | \.test.ps1
Every example above that doesn't produce an error always prompts the user instead of accepting the piped input.
Note: My powershell version table says 4.0.-1.-1
Thanks
Edit/Result: To those looking for a solution, you cannot pipe input to a powershell script. You have to update your PS file. See the snippets below.
The issue is that your script \.test.ps1 is not expecting the value.
Try this:
param(
[parameter(ValueFromPipeline)]$string
)
# Edit: added if statement
if($string){
$in = "$string"
}else{
$in = read-host -prompt "input"
}
Write-Host $in
Alternatively, you can use the magic variable $input without a param part (I don't fully understand this so can't really recommend it):
Write-Host $input
You can't pipe input to Read-Host, but there should be no need to do so.
PowerShell doesn't support input redirection (<) yet. In practice this is not a (significant) limitation because a < b can be rewritten as b | a (i.e., send output of b as input to a).
PowerShell can prompt for input for a parameter if the parameter's value is missing and it is set as a mandatory attribute. For example:
function test {
param(
[parameter(Mandatory=$true)] [String] $TheValue
)
"You entered: $TheValue"
}
If you don't provide input for the $TheValue parameter, PowerShell will prompt for it.
In addition, you can specify that a parameter accepts pipeline input. Example:
function test {
param(
[parameter(ValueFromPipeline=$true)] [String] $TheValue
)
process {
foreach ( $item in $TheValue ) {
"Input: $item"
}
}
}
So if you write
"A","B","C" | test
The function will output the following:
Input: A
Input: B
Input: C
All of this is spelled out pretty concisely in the PowerShell documentation.
Yes; in Powershell 5.1 "<" is not implemented (which sucks)
so, this won't work: tenkeyf < c:\users\marcus\work\data.num
but,
this will: type c:\users\marcus\work\data.num | tenkeyf
...
PowerShell doesn’t have a redirection mechanism, but.NET have.
you can use [System.Diagnostics.Process] implements the purpose of redirecting input.
The relevant Microsoft documents are as follows.
Process Class
This is a sample program that works perfectly on my windows 10 computer
function RunAndInput{
$pi = [System.Diagnostics.ProcessStartInfo]::new()
$pi.FileName ="powershell"
$pi.Arguments = """$PSScriptRoot\receiver.ps1"""
$pi.UseShellExecute = $false
$pi.RedirectStandardInput = $true
$pi.RedirectStandardOutput = $true
$p = [System.Diagnostics.Process]::Start($pi)
$p.StandardInput.WriteLine("abc"+ "`r`n");
$p.StandardOutput.ReadToEnd()
$p.Kill()
}
RunAndInput
# OutPut
Please enter the information: abc
Received:abc
# receiver.ps1
('Received:' + (Read-Host -Prompt 'Please enter the information'))
Hope to help you!

Different script results from identical shortcut and console input

I am trying to input 2 args from a shortcut and automatically assign them like this:
powershell.exe -NoExit -NoProfile -File d:\scripts\swap-parms.ps1 A 7
This script works perfectly when run from the console, but not from a shortcut!
[int]$digit = 6 #Default values.
[String]$variable = 'X'
Write-Host $args[0].GetType().Name #Shows both as String from hortcut!
Write-Host $args[1].GetType().Name
switch($args) { #Allows any order of input parameters and assigns.
{$_.GetType().Name -eq 'Int32'} {$digit = $_ ; continue}
{$_.GetType().Name -eq 'String'} {$variable = $_}
}
$digit
$variable
I know I can do this with named parameters like '-var B -dig 2' but, as it works perfectly from the console I can't understand why it fails with identical args from the shortcut. Ideas please?
Use -command instead of -file. For proof:
==> powershell -noprofile -file D:\PShell\SO\swap-parms.ps1 A 33
String
String
6
33
==> powershell -noprofile -command D:\PShell\SO\swap-parms.ps1 A 33
String
Int32
33
A
==>

Executing Powershell script from command line with quoted parameters

I am automating the build of a legacy MS Access application, and in one of the steps, I am trying to make an Access executable (.ADE). I have come up with the following code, which is stored in a file (PSLibrary.ps1):
Add-Type -AssemblyName Microsoft.Office.Interop.Access
function Access-Compile {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
Write-Output "Starting MS Access"
$access = New-Object -ComObject Access.Application
$access.Visible = $FALSE
$access.AutomationSecurity = 1
if (!(Test-Path $source)) { Throw "Source '$source' not found" }
if ((Test-Path $destination)) {
Write-Output "File '$destination' already exists - deleting..."
Remove-Item $destination
}
Write-Output "Compiling '$source' to '$destination'"
$result = $access.SysCmd(603, $source, $destination)
$result
Write-Output "Exiting MS Access"
$access.quit()
}
If I go into the PowerShell ISE and run the command below, then everything works fine, and the expected output is created:
PS C:>& "C:\Temp\PSLibrary.ps1"
PS C:>Access-Compile "C:\Working\Project.adp" "C:\Working\Project.ade"
However, I can't seem to generate the right hocus-pocus to get this running from the command line, as I would in an automated build. For instance,
powershell.exe -command "& \"C:\\Temp\\PSLibrary.ps1\" Access-Compile \"C:\\Temp\\Project.adp\" \"C:\\Temp\\Project.ade\""
What am I doing wrong?
For complex parameters, you can use Powershell's -EncodedCommand parameter. It will accept a Base64 encoded string. No escaping is needed for quotes, slashes and such.
Consider a test function that will print its parameters. Like so,
function Test-Function {
param (
[Parameter(Mandatory=$TRUE,Position=1)][string]$source,
[Parameter(Mandatory=$TRUE,Position=2)][string]$destination
)
write-host "src: $source"
write-host "dst: $destination"
}
Create command to load the script and some parameters. Like so,
# Load the script and call function with some parameters
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
After the command syntax is OK, encode it into Base64 form. Like so,
[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes('. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""'))
You'll get a Base64 string. Like so,
LgAgAEMAOgBcAFQAZQBtAHAAXABDAGEAbABsAGkAbgBnAC0AVABlAHMAdAAuAHAAcwAxADsAIAAgAFQAZQBzAHQALQBGAHUAbgBjAHQAaQBvAG4AIAAiAHMAbwBtAGUAXABzAHAAZQBjAGkAYQBsADoAYwBoAGEAcgBhAGMAdABlAHIAcwA/ACIAIAAiAGAAIgBjADoAXABtAHkAIABwAGEAdABoAFwAdwBpAHQAaABcAHMAcABhAGMAZQBzACAAdwBpAHQAaABpAG4ALgBlAHgAdABgACIAIgA=
Finally, start Powershell and pass the encoded string as a parameter. Like so,
# The parameter string here is abreviated for readability purposes.
# Don't do this in production
C:\>powershell -encodedcommand LgAgA...
Output
src: some\special:characters?
dst: "c:\my path\with\spaces within.ext"
Should you later want to reverse the Base64 encoding, pass it into decoding method. Like so,
$str = " LgAgA..."
[Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($str))
# Output
. C:\Temp\Calling-Test.ps1; Test-Function "some\special:characters?" "`"c:\my path\with\spaces within.ext`""
PowerShell like Bash can take single or double quotes
PS C:\Users\Steven> echo "hello"
hello
PS C:\Users\Steven> echo 'hello'
hello
this can alleviate some of the headache, also I think you can use the literal backslashes without escaping.
To run PowerShell, choose
Start Menu Programs Accessories
Windows Powershell Windows Powershell

How do I pass an array parameter to powershell -file?

I have the following powershell script:
param(
[Int32[]] $SomeInts = $null,
[String]$User = "SomeUser",
[String]$Password = "SomePassword"
)
New-Object PSObject -Property #{
Integers = $SomeInts;
Login = $User;
Password = $Password;
} | Format-List
If I execute .\ParameterTest.ps1 (1..10) I get the following:
Password : SomePassword
Login : SomeUser
Integers : {1, 2, 3, 4...}
However, I don't get the expected results if I run it in a separate powershell instance like this powershell -file .\ParameterTest.ps1 (1..10). In that case I get the following:
Password : 3
Login : 2
Integers : {1}
My question is how can I pass the array, or other complex data type from a command line?
The individual elements of the array (1..10) are being passed as parameters to the script.
An alternative would be to do:
powershell -command {.\test.ps1 (1..10)}
For a version that works both from powershell console and cmd:
powershell -command "&.\test.ps1 (1..10)"
The answer is to use powershell.exe -EncodedCommand and to base64 encode the parameters. The description for this is on the PowerShell.exe Console Help technet page. I have compressed their version of the ceremony to do this into a one-liner:
powershell.exe -EncodedCommand "$([Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('.\ParameterTest.ps1 (1..10)')))"