Invoke powershell script from another - powershell

How can I invoke a powershell script from within another script?
This is not working:
$param1 = "C:/Users/My Folder/file1"
$param2 = "C:/Users/My Folder/file2"
$command = "C:/Users/My Folder/second.ps1"
Invoke-expression $command -File1 $param1 -File2 $param2
...
Second.ps1:
param(
[string]File1, [string]File2)...

If there are no spaces:
Invoke-expression "$command $param1 $param2"
If you know where the spaces are:
Invoke-expression "$command `$param1HasSpaces` $param2"
NB: If your execution policy is restricted (check with get-executionpolicy use:
Invoke-Expression "powershell -executionpolicy bypass -command `"$command $param1 $param2`""

You can do it like this if you slightly change your approach. Basically create the command string you want to execute, then create a scriptblock object from that and then use Invoke-Command instead of Invoke-Expression.
$param1 = "C:/Users/My Folder/file1"
$param2 = "C:/Users/My Folder/file2"
$command = "C:/Users/My Folder/second.ps1"
$str = '{0} -File1 "{1}" -File2 "{2}"' -f ($command, $param1, $param2)
$sb = [scriptblock]::Create($str)
Invoke-Command -ScriptBlock $sb

Related

Pass complex arguments to powershell script through encoded command

I want to run nested powershell script block, with complex type arguments. I want to pass parameters to powershell scriptblock trough encoded command and encoded arguments.
I`m trying this script.
$text = "This is a test message."
$Cred = get-credential 'alex'
$Arguments = #{
Msg = $Text
Proc = $PID
Cred = $Cred
}
$Serialized = [System.Management.Automation.PSSerializer]::Serialize($Arguments)
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Serialized)
$EncodedArguments = [Convert]::ToBase64String($Bytes)
$ScriptBlock = {
param([String]$Base64)
$Serialized = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($Base64))
$Arguments = [System.Management.Automation.PSSerializer]::Deserialize($Serialized)
Write-Host " $($Arguments.msg) FROM PID: $($Arguments.proc), cred: $( $Arguments.Cred.Username)"
}
$Bytes = [System.Text.Encoding]::Unicode.GetBytes( $ScriptBlock.ToString() )
$EncodedCommand = [Convert]::ToBase64String( $Bytes )
Start-Process -FilePath powershell -ArgumentList '-noprofile', '-noexit', `
'-EncodedCommand', $EncodedCommand, '-EncodedArguments', $EncodedArguments
Powershell process flashing, then closing.
Help me to correct this script.
PS. Why stack overflow editor replace the header of my message 'Hello team!' ? Its all right on preview.
A -EncodedArguments parameter (or something alike) doesn't exist, therefore I would simply embed your arguments as a default param value in your (encoded) scriptblock:
$Arguments = #{
Msg = "This is a test message."
Proc = $PID
Cred = (Get-Credential 'Alex')
}
$Serialized = [System.Management.Automation.PSSerializer]::Serialize($Arguments)
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Serialized)
$EncodedArguments = [Convert]::ToBase64String($Bytes)
$ScriptBlock = "param([String]`$EncodedArguments = '$EncodedArguments')" + {
$Serialized = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($EncodedArguments))
$Arguments = [System.Management.Automation.PSSerializer]::Deserialize($Serialized)
Write-Host " $($Arguments.msg) FROM PID: $($Arguments.proc), cred: $( $Arguments.Cred.Username)"
}
$Bytes = [System.Text.Encoding]::Unicode.GetBytes( $ScriptBlock.ToString() )
$EncodedCommand = [Convert]::ToBase64String( $Bytes )
Start-Process -FilePath powershell -ArgumentList '-noprofile', '-noexit', '-EncodedCommand', $EncodedCommand

Elevate a script with parameters

I have a Powershell script with parameters that I'd like to be able to self-elevate.
[CmdletBinding()]
param (
[Parameter(ParameterSetName="cp")]
[Switch]
$copy = $false,
[Parameter(ParameterSetName="mv")]
[Switch]
$move = $false
)
# Elevate if required
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
$Cmd = (
'-File',
"`"$($MyInvocation.MyCommand.Path)`"",
$MyInvocation.BoundParameters
)
$ProcArgs = #{
FilePath = 'PowerShell.exe'
Verb = 'RunAs'
ArgumentList = $Cmd
}
Start-Process #ProcArgs
Exit
}
}
Set-Location -LiteralPath $PSScriptRoot
Write-Host "$copy"
Pause
If I comment out the param block and run script.ps1 -copy, an elevated Powershell window opens and prints out Press enter to continue, i.e. it works.
If I comment out the if statement, the current window outputs True, i.e. it also works.
If I run the whole thing though, the elevated windows opens for a split second, then closes ignoring Pause with no output anywhere.
I want the elevated window to open and print out True.
I tested this on Linux and worked for me but couldn't test on Windows, I don't see other way around having to manipulate the $PSBoundParameters into strings to pass the arguments on -ArgumentList.
Below code is meant to be exclusively for testing, hence why I've removed the if conditions.
[CmdletBinding()]
param (
[Parameter(ParameterSetName="cp")]
[Switch]$copy,
[Parameter(ParameterSetName="mv")]
[Switch]$move
)
$argument = #(
"-File $PSCommandPath"
"-$($PSBoundParameters.Keys)"
)
$ProcArgs = #{
FilePath = 'powershell.exe'
Verb = 'RunAs'
ArgumentList = $argument
}
Start-Process #ProcArgs
"Started new Process with the argument: -$($PSBoundParameters.Keys)"
[System.Console]::ReadKey()
Exit

How to Start process with Powershell [System.Diagnostics.Process]::Start with more the one parameter

When I start a process in PS:
This works fine.
[System.Diagnostics.Process]::Start("myprog.exe", "$param1 $param2 /run /SilentMode")
But if $param2 has a space in it will be truncated at the first word. Myprog.exe will insert the 2 strings passed as a parameter in a SQL table.
So this works fine:
$param1 = "param1string"
$param2 = "param2string"
[System.Diagnostics.Process]::Start("myprog.exe", "$param1 $param2 /run /SilentMode")
But here not. I will have on the table only the first word of the second parameter.
$param1 = "param1string"
$param2 = "param2 string string"
[System.Diagnostics.Process]::Start("myprog.exe", "$param1 $param2 /run /SilentMode")
How can I fix this?
The correct way to double quote a parameter is:
$param1 = "param1string"
$param2 = "`"param2 string string`""
[System.Diagnostics.Process]::Start("myprog.exe", "$param1 $param2 /run /SilentMode")
It works for me!
Use start-process https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6
look the examples
Start-Process -FilePath "$env:comspec" -ArgumentList "/c dir `"%systemdrive%\program files`""
Start-Process -FilePath "$env:comspec" -ArgumentList "/c","dir","`"%systemdrive%\program files`""

How to run a program as another user and add arguments in powershell?

We have a program that only updates when being run with the switch /t from an administrator account.
I came up with the CMD prompt version, but I'm new to powershell and having a hard time translating it to Powershell.
The CMD version is:
C:\Windows\System32\runas.exe /savecred /user:ourdomain\ouruseracct "C:\Program Files (x86)\ProjectMatrix\ProjectNotify\ProjectNotify.exe /t"
So far I got:
C:\Windows\System32\runas.exe /user:ourdomain\ouruseracct /savecred "powershell -c start-process -FilePath \"'C:\Program Files (x86)\ProjectMatrix\ProjectNotify\ProjectNotify.exe'\" -verb runAs"
Which runs powershell as admin and starts the program as admin but we need to pass the argument -t or /t to projectnotify.exe when running it.
I believe we need to make use of the -argumentlist but not sure how to word it.
I tried
$t = "-t"
Start-Process -FilePath "C:\Program Files (x86)\ProjectMatrix\ProjectNotify\projectnotify.exe" -ArgumentList $t -Verb runas
Which runs the program but not sure if that's how you pass the argument.
Extra work (troubleshooting):
$Cred = Get-Credential
$ProcInfo = New-Object -TypeName 'System.Diagnostics.ProcessStartInfo'
$ProcInfo.Domain = $Cred.GetNetworkCredential().Domain
$ProcInfo.UserName = $Cred.UserName
$ProcInfo.Password = $Cred.Password
$ProcInfo.FileName = "${Env:ProgramFiles(x86)}\ProjectMatrix\ProjectNotify\ProjectNotify.exe"
$ProcInfo.Arguments = '/t'
$ProcInfo.WorkingDirectory = "${Env:ProgramFiles(x86)}\ProjectMatrix\ProjectNotify"
$ProcInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal
$ProcInfo.Verb = 'RunAs'
$ProcInfo.UseShellExecute = $true
[System.Diagnostics.Process]::Start($ProcInfo)
After some more thought, here's a simpler way (in a single command even):
Start-Job -Credential (Get-Credential) -ScriptBlock {
$Dir = "${Env:ProgramFiles(x86)}\ProjectMatrix\ProjectNotify"
$StartArgs = #{
'FilePath' = "$Dir\ProjectNotify.exe"
'ArgumentList' = '/t'
'Verb' = 'RunAs'
'WindowStyle' = 'Normal'
'WorkingDirectory' = $Dir
'PassThru' = $true
}
Start-Process #StartArgs
} | Wait-Job | Receive-Job
My previous answer is at the bottom of this post now.
References:
about_Splatting
Get-Credential
Start-Process
Start-Job
Extra reading:
Import-CliXml
Export-CliXml
Assuming an on-demand script, you should create a pscredential object if you want to natively run this from powershell:
Launch.cmd
SET "PS=%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe"
SET "SCRIPT=%SYSTEMDRIVE%\Path\to\wrapper.ps1"
%PS% -NoProfile -NoLogo -ExecutionPolicy Bypass -File "%SCRIPT%"
wrapper.ps1
$Cred = Get-Credential
# To avoid prompting every time:
#
# if (-not (Test-Path -Path '.\mycred.xml')) {
# Get-Credential | Export-CliXml -Path '.\mycred.xml'
# }
# $Cred = Import-CliXml -Path '.\mycred.xml'
$StartArgs = #{
'FilePath' = "$PSHOME\powershell.exe"
'ArgumentList' = '-NoProfile', '-NoLogo', '-File', '.\runas.ps1'
'Credential' = $Cred
}
Start-Process #StartArgs
runas.ps1
$StartArgs = #{
'FilePath' = "${Env:ProgramFiles(x86)}\ProjectMatrix\ProjectNotify\ProjectNotify.exe"
'ArgumentList' = '/t'
'Verb' = 'RunAs'
}
Start-Process #StartArgs
I know the question asks for arguements, but if you don't them, this works:
Start cmd.exe -Verb RunAs
You can also run this using the 'Run' window or search box:
powershell -command start cmd.exe -verb runas

How can I pass an argument to a PowerShell script?

There's a PowerShell script named itunesForward.ps1 that makes iTunes fast forward 30 seconds:
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + 30
}
It is executed with a prompt line command:
powershell.exe itunesForward.ps1
Is it possible to pass an argument from the command line and have it applied in the script instead of the hardcoded 30 seconds value?
Tested as working:
#Must be the first statement in your script (not counting comments)
param([Int32]$step=30)
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + $step
}
Call it with
powershell.exe -file itunesForward.ps1 -step 15
Multiple parameters syntax (comments are optional, but allowed):
<#
Script description.
Some notes.
#>
param (
# height of largest column without top bar
[int]$h = 4000,
# name of the output image
[string]$image = 'out.png'
)
And some example for advanced parameters, e.g. Mandatory:
<#
Script description.
Some notes.
#>
param (
# height of largest column without top bar
[Parameter(Mandatory=$true)]
[int]$h,
# name of the output image
[string]$image = 'out.png'
)
Write-Host "$image $h"
A default value will not work with a mandatory parameter. You can omit the =$true for advanced parameters of type boolean [Parameter(Mandatory)].
You can use also the $args variable (that's like position parameters):
$step = $args[0]
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + $step
}
Then it can be called like:
powershell.exe -file itunersforward.ps1 15
Call the script from a batch file (*.bat) or CMD
PowerShell Core
pwsh.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 -Param1 Hello -Param2 World"
pwsh.exe -NoLogo -ExecutionPolicy Bypass -Command "path-to-script/Script.ps1 -Param1 Hello -Param2 World"
pwsh.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 Hello -Param2 World"
pwsh.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 Hello World"
pwsh.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 -Param2 World Hello"
PowerShell
powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 -Param1 Hello -Param2 World"
powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "path-to-script/Script.ps1 -Param1 Hello -Param2 World"
powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 Hello -Param2 World"
powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 Hello World"
powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "./Script.ps1 -Param2 World Hello"
Call from PowerShell
PowerShell Core or Windows PowerShell
& path-to-script/Script.ps1 -Param1 Hello -Param2 World
& ./Script.ps1 -Param1 Hello -Param2 World
Script.ps1 - Script Code
param(
[Parameter(Mandatory=$True, Position=0, ValueFromPipeline=$false)]
[System.String]
$Param1,
[Parameter(Mandatory=$True, Position=1, ValueFromPipeline=$false)]
[System.String]
$Param2
)
Write-Host $Param1
Write-Host $Param2
Let PowerShell analyze and decide the data type. It internally uses a 'Variant' for this.
And generally it does a good job...
param($x)
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + $x
}
Or if you need to pass multiple parameters:
param($x1, $x2)
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + $x1
$iTunes.<AnyProperty> = $x2
}
# ENTRY POINT MAIN()
Param(
[Parameter(Mandatory=$True)]
[String] $site,
[Parameter(Mandatory=$True)]
[String] $application,
[Parameter(Mandatory=$True)]
[String] $dir,
[Parameter(Mandatory=$True)]
[String] $applicationPool
)
# Create Web IIS Application
function ValidateWebSite ([String] $webSiteName)
{
$iisWebSite = Get-Website -Name $webSiteName
if($Null -eq $iisWebSite)
{
Write-Error -Message "Error: Web Site Name: $($webSiteName) not exists." -Category ObjectNotFound
}
else
{
return 1
}
}
# Get full path from IIS WebSite
function GetWebSiteDir ([String] $webSiteName)
{
$iisWebSite = Get-Website -Name $webSiteName
if($Null -eq $iisWebSite)
{
Write-Error -Message "Error: Web Site Name: $($webSiteName) not exists." -Category ObjectNotFound
}
else
{
return $iisWebSite.PhysicalPath
}
}
# Create Directory
function CreateDirectory([string]$fullPath)
{
$existEvaluation = Test-Path $fullPath -PathType Any
if($existEvaluation -eq $false)
{
new-item $fullPath -itemtype directory
}
return 1
}
function CreateApplicationWeb
{
Param(
[String] $WebSite,
[String] $WebSitePath,
[String] $application,
[String] $applicationPath,
[String] $applicationPool
)
$fullDir = "$($WebSitePath)\$($applicationPath)"
CreateDirectory($fullDir)
New-WebApplication -Site $WebSite -Name $application -PhysicalPath $fullDir -ApplicationPool $applicationPool -Force
}
$fullWebSiteDir = GetWebSiteDir($Site)f($null -ne $fullWebSiteDir)
{
CreateApplicationWeb -WebSite $Site -WebSitePath $fullWebSiteDir -application $application -applicationPath $dir -applicationPool $applicationPool
}
Create a PowerShell script with the following code in the file.
param([string]$path)
Get-ChildItem $path | Where-Object {$_.LinkType -eq 'SymbolicLink'} | select name, target
This creates a script with a path parameter. It will list all symbolic links within the path provided as well as the specified target of the symbolic link.
You can also define a variable directly in the PowerShell command line and then execute the script. The variable will be defined there, too. This helped me in a case where I couldn't modify a signed script.
Example:
PS C:\temp> $stepsize = 30
PS C:\temp> .\itunesForward.ps1
with iTunesForward.ps1 being
$iTunes = New-Object -ComObject iTunes.Application
if ($iTunes.playerstate -eq 1)
{
$iTunes.PlayerPosition = $iTunes.PlayerPosition + $stepsize
}