i found an old question on this topic. However, i am not clear.
I have a script that checks, if PS has been run using "run as administrator" and if yes it does the job, otherweise it prompts that the script must be run as administrator.
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$CheckforAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
it gives true or false. I have if-else statement that does the rest.
If($CheckforAdmin -eq 'True'){
$MSG = ""
If(($EventLogCheck -ne $EventLog) -or ($EventLogsourceCheck -ne 'True')){
New-EventLog -LogName $EventLog -Source $EventLogSource -ErrorAction Stop
$MSG = "$env:USERNAME has successfully created a new eventlog named $EventLog with the source $EventLogSource."
Write-EventLog -logname $PiEventLog -source $PiEventLogSource -eventID 1021 -entrytype Information -message $MSG
}
else{
$MSG = "$env:USERNAME tried to create an EventLog named $EventLog with the source $EventLogSource. `nSince the EventLog and the source already exist, this step was aborted."
Write-EventLog -logname $EventLog -source $EventLogSource -eventID 1021 -entrytype Information -message $MSG
}
# Wenn der Parameter Silent auf true gesetzt ist, wird das Skript nach der Erstellung des EventLogs unmittelbar beendet.
if($install -eq $true){
Write-Host $MSG
Read-Host 'Press any key to continue...'
}
exit
}
else{
Write-Host "The Script must be executed as admin"
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
[System.Windows.Forms.MessageBox]::Show('Installation: The script must be run as administrator in order to create the event log', 'Run as admin')
exit
}
It works well, if i am logged in with a normal user. But on my server where i want to run the script, i log in as domain administrator. Even if if run the script just double clicking on it, it runs instead of prompting that the script must be run using "run as administrator".
I red the articles about UAC (User Account control) and as far as i understood: running a script using "run as administor" is actually the same as logging in as domain administrator and double clicking on the script.
Is there any other way to check, if the script was run using "run as administrator" option that shows up if u right click on powershell (doesn't matter, whether you are logged in as admin or not) ?
At the top of your script add the line:
#Requires -RunAsAdministrator
then remove all your code to check for an administrator.
If the user running the script is not an elevated administrator, a message will be displayed and the execution of the scripts stops.
Original comment:
How did you implement the prompt? Because obviously, this part only returns $true or $false. I suppose a way to work around this would be something like
PowerShell -NoProfile -ExecutionPolicy Unrestricted -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Unrestricted -File ""PS_Script_Path&Name.ps1""' -Verb RunAs}"
To call the script as admin again, if it wasnt before.
Newly added:
Additionally, you have an error in your if-statement. To compare to boolean, you want to have your if-statement like following:
If($CheckforAdmin -eq $true){
Comparing against strings can lead to problems. Otherwise I cannot locate any other errors.
Related
I have a PowerShell script that restarts the Spooler service, then locates all printers with a specific name, removes those printers, then adds the printers back. I want to be able to go on a user's PC and run the script and input my admin credentials, but when I do, it doesn't find the printers because printers are per user not per PC. Is there a way to run the script as the user with elevated permissions in a single PS instance?
# Check if ps is running as admin otherwise open this script as admin
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }
# Local printer name to search for
$FindName = "*DSM*"
# Store local printer object for later use
$Printers = Get-Printer -Name $FindName
if(!$Printers){
$NotFound = "No printer with the name "
$NotFound2 = " was found."
$NotFound + $FindName + $NotFound2
}else{
Write-Output "Printer found"
# Restart spooler service
Write-Output "Restarting Spooler"
Restart-Service -Name Spooler -Force
# Wait for Spooler to come back online
Start-Sleep -s 5
# loop through all printers found and re-add each one
foreach($Printer in $Printers){
# Remove printer
Write-Output "`nRemoving " $Printer.Name
Remove-Printer -Name $Printer.Name
# Add printer
Write-Output "Re-adding " $Printer.Name
Add-Printer -ConnectionName $Printer.Name
}
Read-Host -Prompt "Press Enter to exit"
}
Basically, you need to run the service management pieces as an admin user (presumably, end users don't have admin on their workstations) but the printer management must happen from the end user's account. You'll essentially use Start-Process to run the service management piece as your admin account, but let the rest of the script run in the end user's context:
# Restart spooler Service
# Splatting used here for readability
$psPath = "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe"
$spArgs = #{
Wait = $True
Credential = Get-Credential DOMAIN.tld\adminuser
FilePath = $psPath
ArgumentList = '-Command "$p = Start-Process -PassThru -Wait -FilePath \"{0}\" -Verb RunAs -ArgumentList \"Restart-Service -Force -EA Stop spooler\"; exit $p.ExitCode"' -f $psPath
PassThru = $True
ErrorAction = 'Stop'
}
$p = Start-Process #spArgs
if( $p.ExitCode -ne 0 ) {
# The service failed to restart. Handle the failure case.
}
The way this works:
Run a new powershell.exe process as your admin user, then making sure to elevate permissions by running powershell.exe a second time with the RunAs Verb. You will need to input the credential each time the script is run.
As indicated in the comments, -Credential and -Verb are mutually exclusive.
There is not really a graceful way to do this, to make this most readable I have use a literal string with the format operator -f to avoid an even worse escape-hell.
The -Command parameter needs to be provided within double-quotes when executed via Start-Process, or else it will only render the literal string and not actually execute the nested command.
The youngest PowerShell process will perform the service restart.
Use the -Wait flag to wait until the process exits for both invocations of Start-Process. Start-Process does not block execution by default regardless of application type.
Make Restart-Service throw a terminating error with -EA Stop if it encounters a problem. This will guarantee a non-zero exit code is returned on failure without requiring additional boilerplate code.
Specifying the full path to powershell.exe is optional but a general good practice. You could also simply use powershell.exe in place of the full path to the binary.
Programs run with Start-Process do not set $LASTEXITCODE. Therefore, we return the Process object with -Passthru, then check that its exit code is 0. Any other exit code indicates failure for this operation.
You can remove your admin check from the script since this no longer needs administrative permissions, and Get-Credential will essentially elevate for the service restart.
Note that if you want to perform this from a user who can elevate but you don't want the PowerShell script itself to run elevated, you can run the script without elevation and use the technique above but omit the -Credential parameter which becomes redundant. It would still work but no reason to authenticate when you don't have to.
I tried to run a Powershell Script under the System Account via Jenkins.
$DebugPreference = 'Continue'
$dt=get-date -Format "MM-dd-yyyy-HH-mm-ss-fff"
Start-Transcript -Path "C:\install\transcript-$dt.txt"
dir env:
$cmdline = $((Get-CimInstance win32_process -Filter "ProcessID=$PID" | ? { $_.processname -eq "pwsh.exe" }).commandline)
if($cmdline -like "*pwsh.exe*")
{
write-host "Powershel 7 continue"
Write-Host "Before Start-session"
$s = New-PSSession -HostName ip -UserName user -verbose -KeyFilePath C:\.ssh\id_rsa
Write-Host "After Start-session"
}else{
Start-Process pwsh.exe -Wait -PassThru -ArgumentList "-NonInteractive -ExecutionPolicy Bypass -File $($MyInvocation.MyCommand.Definition)"
}
stop-transcript
My Problem is that Write-Host "After Start-session" is never reached.
The first Start-Transcript shows, that the Script is started again with pwsh.exe
The second Start-Transcript shows the Output till Before Start-session.
After that there is nothing added to the Transcript and the Process keeps running.
The Script is working fine, when it is running under the Administrator Account.
How can I debug this ?
The Problem was that the SSH fingerprint was not trusted.
When I runned the Script via a command Line instead of via Jenkins direct, i got this output:
The authenticity of host 'ip (ip)' can't be established.
ECDSA key fingerprint is SHA256:gQv8WE8G04RhfNNX7pRQjVX0lPj3jNZ4JTPIDNEIGHk.
Are you sure you want to continue connecting (yes/no)?
After i answered it with yes everything worked.
The Jenkings Job is now working two.
I am trying to run a powershell script from the user data box when creating an ec2 instance from a custom AMI. I have enabled user data execution on the config before creating the ami.
This is what i put into user data
<powershell>
c:\scripts\github-download.ps1 someuser somepassword
</powershell>
The script it is calling is shown below.
Param($gituser, $gitpass)
C:\Users\Administrator\AppData\Local\GitHub\shell.ps1
git clone https://"$gituser":"$gitpass"#github.com/somegitrepo |out-null
I have no idea why this isn't working. Am i doing something wrong here? Any help really appreciated.
Instead of calling the user data using the <powsershell> tag, call PowerShell itself using the <script> tag. You gain command line control over its invocation, and can control execution policy and other command line settings directly:
<script>
PowerShell -ExecutionPolicy Bypass -NoProfile -File c:\scripts\github-download.ps1 -user USER -password PASSWORD
</script>
In your script, setup the beginning and end sections of your script as below:
# Server script called from userdata in this format
# <script>
# PowerShell -ExecutionPolicy Bypass -NoProfile -File c:\scripts\github-download.ps1 -user USER -password PASSWORD
# </script>
param (
[string]$user = $(throw "-user is required."),
[string]$password = $(throw "-password is required."),
)
Start-Transcript -Path C:\userscriptlog.txt
Import-Module WebAdministration
if ([System.Diagnostics.EventLog]::SourceExists("Userdata") -eq $False) {
New-Eventlog -Logname Application -Source 'Userdata'
}
Write-Eventlog -Logname Application -Source 'Userdata' -EventId 1 -EntryType Information -Message 'Begining post-deployment configuration script'
-- YOUR MAIN SCRIPT HERE --
Write-Eventlog -Logname Application -Source 'Userdata' -EventId 1 -EntryType Information -Message 'Post-deployment configuration script complete'
Stop-Transcript
For error handling in your script, you need to use robust exception handling and logging for each command, again to make troubleshooting and debugging easy. This block simply gets the current instance ID, but note the exception handling and logging built in:
# get instance-id
try {
$InstanceId = (Invoke-WebRequest http://169.254.169.254/latest/meta-data/instance-id).content
} catch {
$_.Exception.message | out-file c:\InstanceId_error.log
Write-Host "FATAL: InstanceId exception"
Exit
}
if (!$InstanceId) {
Write-Host "FATAL: InstanceId is null"
Exit
} else {
$InstanceId | out-file C:\InstanceId.txt
Write-Host "InstanceId: $InstanceId"
}
Try that approach to any command or shell invocation that you need to implement.
This powershell script 'wrapper' for user data scripts allows optional command line parameters, produces a transcript of execution, and logs events to the Windows event log, to confirm basic execution of the script.
It will provide a flexible framework for any Powershell based user data script, allow for easy debugging and testing.
| out-null silences any errors that could be happening with git clone so you won't know what is wrong unless you pipe the error somewhere else or just don't use | out-null.
I would manually run the command on the EC2 instance without the | out-null before you try and use user data to automate anything.
I'm creating a local PowerShell module downloader script. The module and the script are held on a network share. The script is invoke using:
& '\\net\DSFShare\Shared Data\Powershell Modules\Install-MyModuleManager.ps1'
It copies the script to the standard profile modules folder and then runs Install.ps1 from the module folder. Install.ps1 elevates itself, if needed. Just before the elevated window closes, a red error pops up, but the window closes too quickly for me to see the error. How can I find out what the error is?
The downloader script invokes the installer using:
$installerPath = [IO.Path]::Combine($LocalModulePath, 'Install.ps1')
Write-Host "Installer path: $installerPath"
if (Test-Path $installerPath) {
Write-Host 'Install.ps1 exists. Running Install.ps1'
& $installerPath
}
Note, if from PowerShell, I populate $installerPath and call it using & $installerPath, I don't see the error.
I've checked the Application, System, Windows PowerShell, and Security event logs. There aren't any errors relating to this.
All the script does is create an event source. If you want to run it, you can use:
Remove-EventLog -Source 'My.Module.Manager'
afterwards, to remove it. Here's the script:
Write-Host "Installing module..."
$eventSource = 'My.Module.Manager'
try {
$sourceExists = [System.Diagnostics.EventLog]::SourceExists($eventSource)
} catch [Security.SecurityException] {
Write-Verbose "Caught 'SecurityException': $_.Exception.Message"
}
if ($sourceExists) {
Write-Host "...installation complete..."
} else {
#region ----- Ensure-ProcessIsElevated -----
if ($Verbose) {
$VerbosePreference = "Continue"
}
if ($Debug) {
$DebugPreference = "Continue"
}
Write-Debug "Command line is ___$($MyInvocation.Line)___"
Write-Verbose "Entering script body"
if ($ScriptPath) {
Set-Location $ScriptPath
Write-Verbose "Working directory: $pwd"
}
If (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Warning "This script must be run with elevated privileges."
Write-Warning "Restarting as an elevated process."
Write-Warning "You will be prompted for authorization."
Write-Warning "You may click 'No' and re-run manually, if you prefer."
If ((Get-WmiObject Win32_OperatingSystem | select BuildNumber).BuildNumber -ge 6000) {
Write-Verbose "This is a UAC-enabled system. Elevating ..."
$CommandLine = "$($MyInvocation.Line.Replace($MyInvocation.InvocationName, $MyInvocation.MyCommand.Definition)) -ScriptPath $pwd"
Write-Verbose "CommandLine: $CommandLine"
Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList "$CommandLine"
} else {
Write-Verbose "The system does not support UAC: an elevated process cannot be started."
Write-Warning "This script requires administrative privileges. Please re-run with administrative account."
}
Break
}
Write-Verbose "The script is now running with elevated privileges."
#endregion ----- Ensure-ProcessIsElevated -----
New-EventLog -LogName Application -Source $eventSource
Write-Host "...installation complete..."
}
I'm using PowerShell 4.0.
You basically have three options to prevent the PowerShell Console window from closing, that I describe in more detail in my blog post.
One-time Fix: Run your script from the PowerShell Console, or launch the PowerShell process using the -NoExit switch. E.g., PowerShell -NoExit "C:\SomeFolder\SomeScript.ps1"
Per-script Fix: Add a prompt for input to the end of your script file. E.g., Read-Host -Prompt "Press Enter to exit"
Global Fix: Change your registry key to always leave the PowerShell Console window open after the script finishes running. Here's the two registry keys that would need to be changed:
● Open With → Windows PowerShell
When you right-click a .ps1 file and choose Open With
Registry Key: HKEY_CLASSES_ROOT\Applications\powershell.exe\shell\open\command
Default Value:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "%1"
Desired Value:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "& \"%1\""
● Run with PowerShell
When you right-click a .ps1 file and choose Run with PowerShell (shows up depending on which Windows OS and Updates you have installed).
Registry Key: HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\0\Command
Default Value:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-Command" "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & '%1'"
Desired Value:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoExit "-Command" "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & \"%1\""
You can download a .reg file from my blog to modify the registry keys for you if you don't want to do it manually.
It sounds like you likely want to use option #2. You could even wrap your whole script in a try block, and only prompt for input if an error occurred, like so:
try
{
# Do your script's stuff
}
catch
{
Write-Error $_.Exception.ToString()
Read-Host -Prompt "The above error occurred. Press Enter to exit."
}
This will make the PowerShell window wait until you press the Enter key (not any key):
pause
Also simple and easy:
Start-Sleep 10
The simplest and easiest way is to execute your particular script with -NoExit param.
1.Open run box by pressing:
Win + R
2.Then type into input prompt:
PowerShell -NoExit "C:\folder\script.ps1"
and execute.
I'm adding the Registry script to leave the PowerShell window open after it completes.
I took it from blog.danskingdom.com
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Applications\powershell.exe\shell\open\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit \"& \\\"%1\\\"\""
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\0\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit \"-Command\" \"if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & \\\"%1\\\"\""
I have a file transcript-test.ps1 with below contents
$log="TestLog{0:yyyyMMdd-HHmm}" -f
(Get-Date) $logfile =
'C:\logs\'+$log+'.txt'
Start-transcript -path $logfile -force
Write-host "To test if this message gets logged"
Stop-transcript
I try to run the script from lets say "box1" and the log file contains the below contents
********** Windows PowerShell Transcript Start Start
time: 20110105114050 Username :
domain\user Machine : BOX1
(Microsoft Windows NT 5.2.3790 Service
Pack 2)
********** Transcript started, output file is
C:\logs\TestLog20110105-1140.txt
To test if this message gets logged
********** Windows PowerShell Transcript End End time:
20110105114050
When I run the same script from another machine using below script I don't see any messages in the log file
Invoke-command {powershell.exe
-ExecutionPolicy Unrestricted -NoProfile -File C:\in ll\transcript-test.ps1} -computername
box1 -credential $credential get-credential
Contents of log file :
********** Windows PowerShell Transcript Start Start
time: 20110105114201 Username :
DOMAIN\user Machine : BOX1
(Microsoft Windows NT 5.2.3790 Service
Pack 2)
********** Windows PowerShell Transcript End End time:
20110105114201
Is there anyway to log the messages from the script to log file when invoked remotely ?
Thanks!
Sanjeev
The following will capture your local and remote verbose messages in the transcript
$VerbosePreference = "continue"
Start-Transcript -path c:\temp\transcript.txt
Write-Verbose "here 1"
$job = Invoke-Command cbwfdev01 -ScriptBlock {
$ErrorActionPreference = "Stop";
$VerbosePreference = "continue";
Write-Verbose "there 1";
Write-Verbose "there 2";
Write-Verbose "there 3";
} -AsJob
Wait-Job $job | Out-Null
$VerboseMessages = $job.ChildJobs[0].verbose.readall()
ForEach ($oneMessage In $VerboseMessages) {Write-Verbose $oneMessage}
Write-Verbose "here 2"
Stop-Transcript
Invoke-command execute your code into a job, which is as far as I understant a thread with no console, so no transcription. For me what you got is absolutly normal.