How to send CLI commands using PowerShell once connected - powershell

I've created an SSH keypair which I'm using to connect to a Pulse VTM (Linux based load balancer appliance) via SSH over Powershell. This part works fine, but I can't seem to send any further commands via my script once the keypair has authenticated. Everything I put in after the authentication seems to get ignored. I'm trying to send the command 'zcli' [enter]. Can someone point me in the right direction for how to stream commands in my script after this point? Invoke-Command doesn't seem to work.
It seems to break at this point:
#Connect to VTM via SSH
ssh admin#**IPADDRESS**
#Initiate ZCLI prompt
Invoke-SSHCommand -Index 0 -Command "zcli"
Script
#Make sure Powershell is running in 64bit mode
if (($pshome -like "*syswow64*") -and ((Get-WmiObject
Win32_OperatingSystem).OSArchitecture -like "64*")) {
write-warning "Restarting script under 64 bit powershell"
# relaunch this script under 64 bit shell
& (join-path ($pshome -replace "syswow64",
"sysnative")\powershell.exe) -file $myinvocation.mycommand.Definition
#args
# This will exit the original powershell process. This will only be
done in case of an x86 process on a x64 OS.
exit
}
#Bypass Execution Policy
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -force
#Connect to VTM via SSH
ssh admin#**IPADDRESS**
#Initiate ZCLI prompt
Invoke-SSHCommand -Index 0 -Command "zcli"
#Create Pool
Pool.addPool ["Sam_Test"],[["10.10.10.10:443","10.10.10.11:80"]]
Pool.setLoadBalancingAlgorithm ["Sam_Test"], roundrobin
Pool.setPassiveMonitoring ["Sam_Test"], 0
Pool.setMonitors ["Sam_Test"], "Simple HTTPS"
Pool.setKeepalive ["Sam_Test"], 1
Pool.setSSLEncrypt ["Sam_Test"], 1
My script logs on fine but then runs no further commands?

Related

Running PowerShell with admin credentials for users

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.

New-PSSession to Linux host is frozen when run under SYSTEM Account

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.

Using Powershell to check proxy setting on remote machine

So i know I can't use Netsh because WinRM is disabled/access denied on my network.
I cant invoke etc.
So I'm trying to figure out a way to take a list of computer names and get the proxy from the machines.
So, my idea is to take the list I have on a \share, and foreach ($pc in $list) I would copy over a .cmd file from the same share. that .cmd file just runs a powershell script from the share, on that remote machine.
So copy a cmd to remote machine. Run cmd on remote machine, that cmd just runs the powershell script that gets the proxy from the registry, and then writes the machine name and the proxy to a file on the share.
But,
I'm getting a blank output tho. So, weird.
Here's the script that runs on the remote machine:
Set-ExecutionPolicy Unrestricted -Force
$proxy = (Get-ItemProperty
'Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings') | Select-Object AutoConfigURL
"$env:computername $proxy" |
Out-file -filepath "\share\Proxy\IEproxylistResolved.csv"
The problem is that I get empty file in IEproxylistResolved.csv its 3am and I'm prob just half dead in the head but could use a fresh perspective.
Have you tried this:
Import-Module PSRemoteRegistry
Get-RegValue -ComputerName $Computer1 -Key
"Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Value AutoConfigURL
Source

PowerShell Invoke-Command using Start-Process is not creating log file

I have a command line exe on a remote server which I need to execute remotely (running on the remote server). This exe connects to the DB, and writes a log file.
I have enabled WinRM by executing these PowerShell commands:
netsh http add iplisten localip {add local ip here}
netsh http add iplisten 127.0.0.1
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
Enable-PSRemoting -Force
This works and I was able to test by executing
Test-WSMan {computername}
I am now trying to execute the command line exe via Invoke-Command and expecting a log file entry to be created with this script.
Invoke-Command –ComputerName MyServer –ScriptBlock {
Start-Process "C:\TheRemotePath\ToTheExe.exe"
}
PowerShell does not display any errors. However, no log file is generated. I have full rights to the directory of the exe as well as the log file directory. I am able to successfully run the exe via Windows Explorer (navigate to the remote exe and double click to run).
Any idea why this does not seem to work or what tools I can use to try and diagnose?
I would try testing the path and switching to the call operator as Ansgar mentioned, i.e.:
Invoke-Command –ComputerName MyServer –ScriptBlock {
$path = "C:\TheRemotePath\ToTheExe.exe"
if (Test-Path $path) {
Write-Host -ForegroundColor Green "Confirmed access to $path!"
& $path
}
else {
Write-Host -ForegroundColor Yellow "Unable to reach $path!"
}
}
Doing those will help you diagnose where it's getting tripped up. Depending on the EXE, I've had to use different methods of starting the process from Powershell.
Change the working directory to the location of your executable before running it. I would also recommend using the call operator (&) instead of Start-Process, unless you have reason to use the latter.
Invoke-Command –ComputerName MyServer –ScriptBlock {
Set-Location 'C:\MyDirectory'
& 'MyApp.Console.exe'
}

How to run w32tm in non-elevated powershell

I am writing a helper script that will go through a list of servers and verify they are in sync with the NTP. The script shall be run as a normal script on request by the facility operator and shall request for Admin credentials if the target is not in sync. We unfortunately cannot change the NTP configuration at the moment so we have to make workaround.
What I have right now (and it works beautifully if the script is run as administrator) is a command ("w32tm /query /status" of a remote computer) that is executed via "Invoke-Command" so I can pass it Admin credentials.
My idea was to avoid using WinRM since the hostname resolution is not working properly in our system (it requires some painful host-to-IP-and-back-to-proper-hostname resolution) which makes the WinRM useless.
The command w32tm can obtain status of a remote computer but it needs to be run as administrator for it.
In both cases (run as administrator and run as normal user and later providing the credentials) the $script is executed as domain\administrator (confirmed with the check of Admin role and the "WhoAmI" command) but the status is only obtained when the whole script is executed as administrator.
For the execution as normal user I receive the error:
The following error occurred: Access is denied. (0x80070005)
All machines I use obviously allow remote execution since it works with administrator user.
So basically my question is why is the "w32tm ..." command not allowed in the $script if the role of the user is appropriate (it is administrator role) for the task?
The part of the script which I can't resolve:
function synchronize_remote_machine ($target, $username, $cred)
{
$script = {
param( [String] $compName )
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
$userIsAdmin = (New-Object Security.Principal.WindowsPrincipal $user).`
IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if (-not $userIsAdmin)
{
Write-Warning "You do not have Administrator rights to run this script!`n
Please re-run this script as an Administrator!"
}
else
{
whoAmI
w32tm /query /status /computer:$compName
#w32tm /resync /rediscover /computer:$compName
}
}
# resync via WinRM
try
{
#execute_resync_command $target $username $cred
if ($username -eq 'Administrator')
{
# when run as admin
invoke-command -ScriptBlock $script -ArgumentList $target;
}
else
{
# for normal user the initalized credential cals is used
invoke-command -computername "localhost" -Credential $cred -ScriptBlock $script -ArgumentList $target
}
}
catch
{
Write-Error "Error executing resync command on host $target."# -foregroundcolor "red"
throw
}
}
Rather than (re-)running the script with elevated privileges, I'd grant the operators group the SeSystemtimePrivilege on those servers. You can do that either with a group policy or by running ntrights.exe from the Server 2003 Resource Kit Tools on each server:
ntrights +r SeSystemtimePrivilege -u DOMAIN\operators
Even if you execute it as administrator, do to try to run you script in an elevated process ?
You can acheive that using Start-Process CmdLet.
start-process 'c:\system32\WindowsPowerShell\v1.0\powershell.exe' -verb runas -argumentlist "-file YourScript.ps1"