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

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.

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.

How to send CLI commands using PowerShell once connected

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?

GPO Startup powershell script not executing

I have a powershell script that I am trying to run at computer startup through a GPO using the new tab for powershell scripts that can be found in the group policy editor.
No matter what, it does not seem to be running at all, and I suspect the problem might for some reason be with the script itself using some var or calling to something that is not available under NT Authority\System impersonation.
Should something in the following script need to be edited in order to actually work as a startup script via GPO?
$sysdrivelocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
#If the drive is encrypted and ready, exit script and do nothing.
if(($sysdrivelocker.VolumeStatus -eq "FullyEncrypted") -or ($sysdrivelocker -eq "EncryptionInProgress")){
exit
}
#If the drive has been prepared with bdehdcfg, start bitlocker encryption and restart the computer.
else if($sysdrivelocker.VolumeStatus -eq "FullyDecrypted"){
#Creating the recovery key
Start-Process 'manage-bde.exe' -ArgumentList " -protectors -add $env:SystemDrive -recoverypassword" -Verb runas -Wait
#Adding TPM key.
Start-Process 'manage-bde.exe' -ArgumentList " -protectors -add $env:SystemDrive -tpm" -Verb runas -Wait
sleep -Seconds 15 #This is to give sufficient time for the protectors to fully take effect.
#Getting Recovery Key GUID.
$RecoveryKeyGUID = (Get-BitLockerVolume -MountPoint $env:SystemDrive).keyprotector | where {$_.Keyprotectortype -eq 'RecoveryPassword'} | Select-Object -ExpandProperty KeyProtectorID
#Backing up the Recovery to AD.
Start-Process 'manage-bde.exe' -ArgumentList " -protectors $env:SystemDrive -adbackup -id $RecoveryKeyGUID" -Verb runas -Wait
#Enabling Encryption.
Start-Process 'manage-bde.exe' -ArgumentList " -on $env:SystemDrive" -Verb runas -Wait
#Restarting the computer, to begin the encryption process.
Restart-Computer
}
#If the drive is not bitlocker ready, prepare it and restart the computer.
else if([string]::IsNullOrEmpty($sysdrivelocker.VolumeStatus) -eq $true)
#Starting the defrag service, required in the next step.
Get-Service -Name defragsvc -ErrorAction SilentlyContinue | Set-Service -Status Running -ErrorAction SilentlyContinue
#Preparing the systemdrive for bitlocker activation, and restarting the computer.
BdeHdCfg -target $env:SystemDrive shrink -quiet -restart | Out-Null
}
#Exit in case the volume status is anything else (e.g. paused or decryption in progress).
else{
exit
}
And yes, before anyone asks, I have set it up correctly as any guide I could find tells me, the script is located under \\domain.local\SysVol\domain.local\Policies\{GPO-GUID}\Machine\Scripts\Startup and for troubleshooting purposes I even set my machines execution policy to unrestricted.

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"

Run powershell commands in parallel

How can I run a PowerShell script in parallel on multiple computers?
$TempFolder = "C:\DOWNLOADED_APPLICATIONS"
if(!(test-path $TempFolder))
{New-Item -path $TempFolder -type directory}
Write-Host ""
Write-Host "Downloading .NET framework v4.0 installation package" -ForegroundColor Yellow;
$src = "\\onesoul\tools\DOTNET45\dotNetFx45_Full_x86_x64.exe"
$dest = "$TempFolder" + "\" + "dotNetFx45_Full_x86_x64.exe"
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($src, $dest)
$args = " /q /norestart"
Write-Host "Installing .NET framework v4.0" -ForegroundColor Yellow;
Start-Process -FilePath $dest -ArgumentList $args -Wait
Write-Output "Dot net 4.5 installed"
This script works fine if I run it remotely on one computer at a time. How do I make it run in parallel?
To run scripts on multiple remote computers you use PowerShell remoting. This requires that every computer you connect to has to have PowerShell remoting enabled which you do with the command:
Enable-PSRemoting -Force
Then from your PC, running an elevated console, use the Invoke-Command to run a script against multiple computers e.g.:
Invoke-Command -Computer server1,server2,server3 -Auth CredSSP `
-FilePath c:\script.ps1 -ArgumentList scriptParameter1, scriptParameter2
I suggest you use the -Auth CredSSP to avoid a second hop issue with your credentials since you're accessing a network share from the remote computer. Also the FilePath parameter will take the path to a local script and copy its contents across the wire to each remote computer. That script should not rely on other scripts unless they exist on all the remote computers. Finally, if you use your credentials, you should admin privs on all the remote computers. If you don't, use the -Credential parameter to provides credentials for an account that does.