How can I pass an argument to a PowerShell script? - powershell

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
}

Related

Elevating Powershell to admin and keeping passed arguments

This was an unsuccessful attempt by me to explain the issue, please scroll down to see a hopefully better explanation
So I'm trying to have my script self-elevate itself to use admin rights.
I think I tried every fix possible on how to elevate a PS session to admin rights but it seems that none can make my arguments stick after the PS session re-opens. I'm assuming it's me who misunderstands how to do this properly.
I have a script that has only one parameter that can be passed to it. Here is the relevant block of code that includes the parameter in the script and the function that I call to elevate to admin permissions:
param (
[Parameter(Mandatory=$false)][bool]$Param1= $false
)
function openWithPriv {
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`";`"$args`"";
exit;
}
}
When I run my script and add my parameter, it just skips that function all together. For example: .\script.ps1 -Param1 $true runs the script in its' entirety but when it reaches my function, it just goes to the Default switch:
function runParam1 {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)][bool]$Param1= $false
)
switch ($Param1) {
$true {
Write-Host "script works"
}
Default {
Write-Host "script didn't run" -ForegroundColor Red
}
}
}
By the way, here is how I call all of the functions in my script, maybe I'm doing something wrong here as well?
#Run all functions
try {
openWithPriv
runParam1 -Param1 $Param1
someFunction1
someFunction2
}
catch {
Write-Host "Unknown error" -ForegroundColor Red -ErrorAction SilentlyContinue
Read-Host -Prompt "Press any key to continue"
}
What am I missing? Any help to fix this would be great :)
This is another attempt to explain, with the full script
So here is my script:
param (
[Parameter(Mandatory=$false)][bool]$param1 = $false
)
function openWithPriv {
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$($PWD.Path)'; & '$PSCommandPath';`";`"$args`"";
exit;
}
}
function func1 {
try {
Write-Host "executing function number 1" -BackgroundColor Blue
}
catch {
Write-Host "Unknown error in func1" -ForegroundColor Red -ErrorAction SilentlyContinue
}
}
function func2 {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)][bool]$param1 = $false
)
switch ($param1) {
$true {
Write-Host "Executing function 2 because param1 was passed"
}
Default {
Write-Host "Skipping func2 because param1 is false" -ForegroundColor Yellow
}
}
}
function func3{
try {
Write-Host "Executing function 3"
}
catch {
Write-Host "Error, couldn't execute func3" -ForegroundColor Red -ErrorAction SilentlyContinue
}
}
#Run all functions
try {
openWithPriv
func1
func2 -param1 $param1
func3
Read-Host "Script finishd without an issue, press any key to exit"
}
catch {
Write-Host "Unknown error somewhere in the script" -ForegroundColor Red -ErrorAction SilentlyContinue
Read-Host -Prompt "Press any key to continue"
}
The issue:
When I execute the script and give it the parameter, nothing happens, it just skips that function and goes to the default switch which is to just prompt the user that the function was skipped.
Example
When I run .\test.ps1, this is the output:
When I run .\test.ps1 -param1 $true
This is the output:
The output should include this text Executing function 2 because param1 was passed as it's shown in func2.
Bottom line
As you can see, because I elevate the script to use admin rights the parameter I'm passing is "lost" when it reaches that function.
Hope this was a bit more clear :)
OK I think you loos the param1 when you start an other process. The $Args are not present and not passed to the script. I splited the code a little to make it clearer
Start-Process PowerShell -Verb RunAs "
-NoProfile -ExecutionPolicy Bypass -Command `"
cd '$pwd';
& '$PSCommandPath';
`";`" # this is just the string ";"
$args # these $args are written after the script is executed
`"
";
Instead you need to pass the $param1 to the script:
Start-Process PowerShell -Verb RunAs "
-NoProfile -ExecutionPolicy Bypass -Command `"
cd '$pwd';
& '$PSCommandPath' -param1 $param1
`"
";
However since $param1 is [bool] and will only accept [bool] you get an error because you are in a string and $param1 will automically be cast to [string] = True instead of $true. To prevent this use 1 and 0 instead:
if ($param1){
$Arg = 1
}else{
$Arg = 0
}
...
Start-Process PowerShell -Verb RunAs "-NoProfile -Noexit -ExecutionPolicy Bypass -Command `"cd '$($PWD.Path)'; & '$PSCommandPath' -param1 $Arg`"";
This could be shortened to:
$Arg = [int]$param1

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

Calling script in elevated mode with more than one named argument

Test.ps1
Param (
[String]$CountryCode,
[String]$FilesPath,
[String]$KeepassDatabase,
[String]$KeepassKeyFile,
[String]$EventLog,
[String]$EventSource
)
Write-Host 'Ok' -ForegroundColor Yellow
Write-Host $PSBoundParameters
Start-Sleep -Seconds 5
The goal is to call the script with named parameters in elevated mode. When using named parameters without $Credential, it works fine. The window pops up and the word Ok is displayed:
$StartParams = #{
ArgumentList = "-File `"Test.ps1`" -verb `"runas`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process powershell #StartParams
When I add the Credential argument it also pops-up but I can't see anything:
$StartParams = #{
Credential = Get-Credential
ArgumentList = "-File `"Test.ps1`" -verb `"runas`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process powershell #StartParams
Am I missing something super obvious here? Even when using the same credentials as the logged on user, I can't see the text.
You need to specify an absolute path to the file. The new PowerShell-process (which will run as admin) doesn't run in the same working directory as your current session.
Try:
$StartParams = #{
FilePath = "powershell.exe"
Credential = Get-Credential
Verb = "RunAs"
ArgumentList = "-File `"c:\temp\Test.ps1`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process #StartParams
If you only know the relative path, use Resolve-Path to convert it. Ex:
ArgumentList = "-NoExit -File `"$(Resolve-Path test.ps1 | Select-Object -ExpandProperty Path)`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
You should also look into string format or here-string so you can avoid escaping every double quote. It makes your life easier:
#Using here-string (no need to escape double quotes)
ArgumentList = #"
-NoExit -File "$(Resolve-Path test.ps1 | Select-Object -ExpandProperty Path)" -FilesPath "S:\Files" -CountryCode "XXX"
"#
#Using string format
ArgumentList = '-NoExit -File "{0}" -FilesPath "{1}" -CountryCode "{2}"' -f (Resolve-Path test.ps1 | Select-Object -ExpandProperty Path), "S:\Files", "XXX"

Passing parameter through powershell.exe

I have written this code
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[Int32]$BoxAlert,
[Parameter(Mandatory=$true)]
[Int32]$MailAlert
)
)
powershell.exe -WindowStyle Hidden {
if ($timeSpan.Days -ge $BoxAlert) {
drawPopupBox $result
}
if ($timeSpan.Days -ge $MailAlert) {
sendMail $result;
}
}
How to pass that $BoxAlert and $MailAlert inside the powershell.exe scriptblock?
Just need to add the -args switch after your scriptblock and a param() definition insides your script block. A simple version is
$x = bar
powershell.exe -command {param($x) write-host "foo, $x"} -args $x
Gives the following output
foo, bar
Applying this logic to your code
PowerShell.exe -WindowStyle Hidden -command {
param($BoxAlert, $MailAlert)
if($timeSpan.Days -ge $BoxAlert)
{
drawPopupBox $result
}
if($timeSpan.Days -ge $MailAlert)
{
sendMail $result;
}
} -args $BoxAlert, $MailAlert

Invoke powershell script from another

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