Elevating Powershell to admin and keeping passed arguments - powershell

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

Related

Powershell: Call function during Start-Process

I have a function called RemoveSoftware:
function RemoveSoftware {
foreach ($utility in $toRemove) {
Try {
winget uninstall -e $utility
$wingetResult.Add("$node`n")
Start-Sleep -s 6
Wait-Process winget -Timeout 90 -ErrorAction SilentlyContinue
}
Catch {
Write-Host "Paket $utility konnte nicht deinstalliert werden. " -ForegroundColor red
}
}
}
This function is in a ps1 file. In the same file I want to start another powershell window which should start this function.
I tried this here:
Start-Process powershell.exe -Verb RunAs -ArgumentList "-command Start-Transcript C:\temp\winget-start.log -Append; & {$RemoveSoftware} | Out-Host" -WindowStyle Normal
But this don't work.
Also I tried several other things like Invoke-Expression and so on. Neither of it worked:(
Has someone a tipp for solving this problem?

How to get the return value of an application in PowerShell?

I am passing an argument to my test application via powershell. I wish to get the return value of the application once it finishes.
How can I get the return value of the application instead of the console output.
I'm running the application by running
Test.ps1 2
Test.ps1
param ([string]$param1)
$path = "C:\Workspaces\myapplication\"
$executable = "Test.exe"
$filepath = "$($path)$($executable) $($param1)"
Try
{
$Result = iex $filepath
#this writes out the console output of Test.exe instead of the return value.
Write-Host $Result
Write-Host $LASTEXITCODE
}
Catch
{
Write-Host "Exit Code"
Write-Host $LASTEXITCODE
}
you should use Start-Process:
$p = Start-Process $($path)$($executable) -ArgumentList $input
$p.HasExited
$p.ExitCode

Restart script as admin and preserve switch and string parameters

I'm writing a PowerShell script that uses parameters / arguments and needs to be run as administrator but I'm trying to make it as user-friendly as possible so I'm trying to write it so that if it wasn't run as administrator then it can auto-elevate itself and preserve the original parameters (only switches and strings).
I was not able to find a solution online, hence this post.
I managed to accomplish this by "stringifying" $PsBoundParameters then using that with Start-Process PowerShell -Verb Runas -ArgumentList.
Note: $PsBoundParameters uses the parameters of the current scope ("root" vs inside a function, for example) so if you need to reference the command-line parameters (as I do) then you'll need to either use this variable outside of a function or first pass the variable to the function (as I've done here).
I've created a demonstration of this:
Param(
[switch]$ExampleSwitch,
[string]$ExampleString
)
Function Restart ($AllParameters, $Admin) {
$AllParameters_String = "";
ForEach ($Parameter in $AllParameters.GetEnumerator()){
$Parameter_Key = $Parameter.Key;
$Parameter_Value = $Parameter.Value;
$Parameter_Value_Type = $Parameter_Value.GetType().Name;
If ($Parameter_Value_Type -Eq "SwitchParameter"){
$AllParameters_String += " -$Parameter_Key";
} Else {
$AllParameters_String += " -$Parameter_Key $Parameter_Value";
}
}
$Arguments = "-File `"" + $PSCommandPath + "`" -NoExit" + $AllParameters_String;
If ($Admin -Eq $True){
Start-Process PowerShell -Verb Runas -ArgumentList $Arguments;
} Else {
Start-Process PowerShell -ArgumentList $Arguments;
}
}
$RanAsAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator);
Write-Host "ExampleSwitch value:" $ExampleSwitch;
Write-Host "ExampleString value:" $ExampleString;
Write-Host "";
If ($RanAsAdministrator -Eq $True){
Write-Host "Running as administrator: Yes.";
} Else {
Write-Host "Running as administrator: No.";
}
$Elevate = Read-Host "Restart as current user or admin? (u/a)";
Write-Host "";
If ($Elevate -Like "u"){
Restart $PsBoundParameters;
} ElseIf ($Elevate -Like "a") {
Restart $PsBoundParameters -Admin $True;
}
Start-Sleep -Seconds 9999;
Param(
[Switch]$MySwitch,
[String]$MyString,
[Switch]$NoExit
)
If (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$PSHost = If ($PSVersionTable.PSVersion.Major -le 5) {'PowerShell'} Else {'PwSh'}
Start-Process -Verb RunAs $PSHost (#(' -NoExit')[!$NoExit] + " -File `"$PSCommandPath`" " + ($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1])
Break
}
Write-Host "MySwitch:" $MySwitch;
Write-Host "MyString:" $MyString;
Explanation:
($MyInvocation.Line -split '\.ps1[\s\''\"]\s*', 2)[-1]) will resolve the current parameters
$MyHost determines the current PowerShell Host (PowerShell for Windows or PowerShell Core) and use the same host for the elevated window.
The -NoExit switch will prevent the elevated window to automatically close
Example:
.\RunAsAdministrator.ps1 -MyString "Test 123" -MySwitch -NoExit

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

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
}