I am trying to replicate this Self-Elevating script in Powershell, but as a function instead of as an entire script so to break up the code for better flow. The original code can be found here.
http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/09/23/a-self-elevating-powershell-script.aspx
However, when I rewrite the same code as a function, it errors out dramatically. Any idea what is causing the fault? This is the new, erroneous code.
function SelfElevation
{
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal ($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "DarkBlue"
$Host.UI.RawUI.ForegroundColor = "White"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess)
# Exit from the current, unelevated, process
Stop-Process -Id $PID
}
}
# We call the self elevation here
SelfElevation
# Run your code that needs to be elevated here
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
This code works fine as I truncate it, right up until the part where it hits Start $newProcess, and it simply does not seem to like the new process being executed in a function. However, why?
Thank you in advance for your time!
The arguments your script takes is likely to be slightly different than what Powershell.exe takes. Try this approach:
[string[]]$argList = #('-NoProfile', '-NoExit', '-File', $MyInvocation.MyCommand.Path)
$argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList
Fix for "white spaces in path" problem and also additional code to set new powershell process path to actual script location
And another fix of error when script is located at mapped network drive
#region Self-Elevating
Set-Location ( Get-Item $script:MyInvocation.MyCommand.Path ).Directory
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal( $myWindowsID )
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
if ( !$myWindowsPrincipal.IsInRole( $adminRole )) {
# This fixes error wen script is located at mapped network drive
$private:scriptFullPath = $script:MyInvocation.MyCommand.Path
if ( $scriptFullPath.Contains([io.path]::VolumeSeparatorChar )) { # check for a drive letter
$private:psDrive = Get-PSDrive -Name $scriptFullPath.Substring(0,1) -PSProvider 'FileSystem'
if ( $psDrive.DisplayRoot ) { # check if it's a mapped network drive
$scriptFullPath = $scriptFullPath.Replace( $psdrive.Name + [io.path]::VolumeSeparatorChar, $psDrive.DisplayRoot )
}
}
[string[]]$argList = #( '-NoLogo', '-NoProfile', '-NoExit', '-File', "`"$scriptFullPath`"" )
$argList += $MyInvocation.BoundParameters.GetEnumerator() | % { "-$( $_.Key )", "$( $_.Value )" }
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $PWD -ArgumentList $argList -PassThru
Stop-Process $PID
}
#endregion
Thanks Keith Hill and Randy in Marin
Related
I have a powershell script which runs an exe and get its output which is working fine, but the issue that i am facing is that if the process is already running then it is not able to capture the output.
Below is my code
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "E:\Program Files (x86)\Consul_Ent\Bin\consul.exe"
$pinfo.Arguments = "agent -config-dir E:\Consul_Ent\Configuration\Client"
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $false
$pinfo.RedirectStandardInput = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.EnvironmentVariables["GOMAXPROCS"] = (Get-WmiObject Win32_Processor | measure -p NumberOfLogicalProcessors -sum).Sum
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
[Void]$process.Start()
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
try
{
while (!$process.WaitForExit(1))
{
}
Write-Host "WaitForExit(1)"
}
finally
{
# give the thread gracefully shutdown
Start-Sleep -s 3
}
so as the process is not terminated yet and hence it is not able to read the output as it tries to create a new instance, i want it to create a new instance only if process is not running but if it is running i just want to capture the output.
I'm writing a program with C# , that can create Users on remote Computers.
Actually it's done and working.
But I have one little problem.
In C# I use PowerShell to run a Script which runs then an Pexec, which executes a Batch file on a remote Computer.
C# :
private void executeScripts()
{
string _dirPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string _sPath = Path.GetDirectoryName(_dirPath) + #"\ExecuteScripts\FileToExecute.ps1";
string _scriptPath = "& '" + _sPath + "'";
using (PowerShellProcessInstance pspi = new PowerShellProcessInstance())
{
string psfn = pspi.Process.StartInfo.FileName;
psfn = psfn.ToLowerInvariant().Replace("\\syswow64\\", "\\sysnative\\");
pspi.Process.StartInfo.FileName = psfn;
using (Runspace r = RunspaceFactory.CreateOutOfProcessRunspace(null, pspi))
{
r.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = r;
ps.AddScript(_scriptPath);
ps.Invoke();
}
}
}
}
PS Script :
#
# First there are some Copy-Items to the remote Computer
#
# Execute Above copied Bat File on remote Computer
[string] $IPAddress = "\\" + $XmlFile.ComputerSettings.LastChild.ChildNodes[1].InnerText
$PsTools = "\PsTools"
$PsToolsPath = Join-Path -path $ScriptParent -childpath $PsTools
& $PsToolsPath\PsExec.exe $IPAddress /accepteula -i -s -u $Login -p $LoginPassword Powershell C:\Path\ToBatFile\Execute.bat > log.txt
Exit
I use this PExec 3 other times in my Program, creating a User, updating a User and removing a User, i just execute different files, scripts or batch files.
And it works perfectly.
But with the Script above, the PExec executes everything but doesn't exit. Neiter does it log something.
I tried it also with the -d switch, but that didn't work either. I also put an exit /b in the batch file but no luck.
When running the script manually from Powershell it works, it executes and it exits, but when running it from my Program it doesn't.
After some waiting my C# returns a timed-out Exception end exits.
Anyone seeing what I'm doing wrong ?
Powershell class itself has a method called Stop() which makes it pretty easy to stop this.
If you want to do it asynchronously here is an example of implementation:
using(cancellationToken.Register(() => powershell.Stop())
{
await Task.Run(() => powershell.Invoke(powershellCommand), cancellationToken);
}
I am calling my exe from powershell script like shown below.
$file = $PSScriptRoot + "\executor.exe"
$code = (Start-Process -WindowStyle Hidden $file -Verb runAs -ArgumentList $Logfile).StandardOutput.ToString;
$nid = (Get-Process "executor.exe").id
Wait-Process -Id $nid
if ($code -eq 1) {
LogWrite "Execution succeeded"
} else
{
LogWrite "Execution Failed"
}
I have a int main function in my exe program which would return 1 on success, and 0 on failure.
When i try to get the ExitCode(using $LASTEXITCODE) from powershell script it always shows null(neither 1 nor 0), But my exe is returning 1 as expected.
How do i capture the return value of exe in powershell script?
You can use this:
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = # path to your exe file
# additional options:
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $false
$psi.WindowStyle = "Maximized"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $psi
$p.Start() | Out-Null # returns $true if the process started, $false otherwise
$p.WaitForExit()
# here's the exitcode
$exitCode = $p.ExitCode
Create the process start info, to specify the executable path, and additional options. It's important to use .WaitForExit() to wait until the process finishes.
What you have tried doesn't get the app exitcode, but what the application write to standard console, which, in your case, I assume is nothing. If you could modify the exe to write to the console, what you did would work.
As picture below, when I am checking"Properties" for any user defined Data Collector sets (Performance Monitor), I can see a "Directory" tab which refers the Path for it.
Is there any C# code or powershell script or any other way to get the same path by just providing user defined Data Collector sets name? Thanks!
Below is the Powershell code you can use just change the path in the code and data collectors as per your need it will automatically start the performance data collector with given process name in the code
first if code is just to bypass the script execution policy in the powershell for any host machine and run the script with the admin rights you can remove that if script execution is enabled
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs;}
Param(
[string]$name = "Test"
)
$datacollectorset = New-Object -COM Pla.DataCollectorSet
$datacollectorset.DisplayName = $name;
$datacollectorset.Duration = 14400 ;
$datacollectorset.SubdirectoryFormat = 1 ;
$datacollectorset.SubdirectoryFormatPattern = "yyyy\-MM";
$datacollectorset.RootPath = "%systemdrive%\PerfLogs\Admin\" + $name ;
$DataCollector = $datacollectorset.DataCollectors.CreateDataCollector(0)
$DataCollector.FileName = $name + "_";
$DataCollector.FileNameFormat = 0x1 ;
$DataCollector.FileNameFormatPattern = "yyyy\-MM\-dd";
$DataCollector.SampleInterval = 10
$counters = #(
"\Memory\Available MBytes",
"\Memory\Page Faults/sec",
"\Memory\Page Reads/sec",
"\Memory\Page Writes/sec",
"\Memory\Pages Input/sec",
"\Memory\Pages Output/sec",
"\Process(CloudHASHService)\*",
"\Processor(_Total)\% Idle Time",
"\Processor(_Total)\% Interrupt Time",
"\Processor(_Total)\% Privileged Time",
"\Processor(_Total)\% Processor Time",
"\Processor(_Total)\% User Time"
) ;
$DataCollector.PerformanceCounters = $counters
try
{
$datacollectorset.DataCollectors.Add($DataCollector)
$datacollectorset.Commit("$name" , $null , 0x0003) | Out-Null
$datacollectorset.Start($false);
}
catch [Exception]
{
Write-Host "Exception Caught: " $_.Exception -ForegroundColor Red
return
}
I would like to ask how to prevent shutdown, when running a script or at least give a popup that will ask whenever or not you want to shutdown (like when you open a notepad and write a char, but doesn't save it and the click shutdown).
I have been creating scripts that runs installers silent, but some of them still seems to activate windows shutdown (this can happen if they are missing prerequisites).
Here is the code I use for the installation:
# --- Install ---
$fileExtension = (Get-ChildItem -path $installationFilePath).Extension
if(".msi" -eq $fileExtension)
{
[string[]]$Private:args = New-Object string[] 4
$args[0] = "/qn"
$args[1] = "TRANSFORM=1033.mst"
$args[2] = "REBOOT=Suppress"
$args[3] = "/l*v $errorLogPath"
$process = Start-Process $installationFilePath -ArgumentList $args -PassThru
}
if(".exe" -eq $fileExtension)
{
[string[]]$Private:args = New-Object string[] 2
$args[0] = '/v"' + "/qn TRANSFORM=1033.mst REBOOT=Suppress /l*v $errorLogPath" + '"'
$args[1] = "/s"
$process = Start-Process $installationFilePath -ArgumentList $args -PassThru
}
$processActive = $process
while ($processActive -ne $null)
{
Start-Sleep -Seconds 1
Write-Host '.' -NoNewline
$processActive = Get-Process -Id $processActive.Id -ErrorAction SilentlyContinue
}
I know this should be possible, but I have yet to find out how.
Here is an example of aborting shutdown after the install has finished:
Start-Process yourprogram.exe -Wait
shutdown /a
You could even loop the abort a few times to make sure you hit it.
for($i=0;$i -lt 5;$i++)
{
shutdown /a
sleep 1
}