Logging scripts from a non-interactive session - powershell

I've been searching for a way of logging a script that is run in a powershell remote session that is similar to what you get from using Start-Transcript. When I try getting a transcript from a remote session, the transcript file gets created, but there is nothing in the file.
$servers = "ServerA"
$session = New-PSSession -ComputerName $servers -Credential $creds -Authentication Default -ErrorAction Stop
Invoke-Command -Session $session -ScriptBlock {
Start-Transcript -OutputDirectory D:\LOGS
Set-Location c:\scripts
Get-ChildItem
Stop-Transcript
Exit-PSSession
}
Remove-PSSession $session
The Transcript file is there, but none of the commands get logged. I know that this is because the scriptblock that is executed on the remote system is a non-interactive session.
What does everyone else do to log any success / failures in their remote scripts?

You don't have to write logfiles on the remote machine. You can collect all information on the caller machine, e.g.
$servers = "ServerA"
$session = New-PSSession -ComputerName $servers -Credential $creds -Authentication Default -ErrorAction Stop
$result = Invoke-Command -Session $session -ScriptBlock {
#Store result into variable
$data = Get-ChildItem -path c:\scripts
#return variable
return $data
}
Remove-PSSession $session
$result on the caller machine will contain the data (objects incl. the hostname of the remote machine) returned from the remote machine.

Related

Invoke-Command executes on remote machine without having an effect

I'm trying to run a command on a VM using Invoke-Command. The command should stop a program that processes jobs after it finishes its current job. It works if I run it in the terminal using RDC.
& 'C:\Program Files\Autodesk\Vault Client 2021\Explorer\JobProcessor.exe' /stop
But if I run it from a different machine using Invoke-Command nothing seems to happen.
$session = New-PSSession -ComputerName 'hostname' -Credential (Get-Credential)
Invoke-Command -Session $session -ScriptBlock {
& 'C:\Program Files\Autodesk\Vault Client 2021\Explorer\JobProcessor.exe' /stop
}
However Process Monitor shows the command come in for both cases, but the program is still running.
I have also tried using Start-Process with the same result, i.e. it works in the terminal on the VM but not using Invoke-Command.
Start-Process -FilePath 'C:\Program Files\Autodesk\Vault Client 2021\Explorer\JobProcessor.exe' -ArgumentList '/stop'
I've been stuck for many days and I've exhausted my googlable knowledge for this problem.
Are you sure that file exists on the remote computer?
For simplicity, I rewrote your command to a known executable that is always there in Windows and returns unique info for any given computer.
C:\> & 'C:\Windows\system32\HOSTNAME.EXE'
server1
C:\> icm {& 'C:\Windows\system32\HOSTNAME.EXE'}
server1
C:\> icm {& 'C:\Windows\system32\HOSTNAME.EXE'} -ComputerName server2
server2
Here's your script with some error handling.
$session = New-PSSession -ComputerName 'hostname' -Credential (Get-Credential)
Invoke-Command -Session $session -ScriptBlock {
$exe = 'C:\Program Files\Autodesk\Vault Client 2021\Explorer\JobProcessor.exe'
$ok = Test-Path $exe
if ($ok) {& $exe /stop} else {
Write-Warning "EXE not present on $($env:COMPUTERNAME)!"
}
}
Learn how to add error handling and you'll be well on your way to solving your problems faster and getting more stuff done.

PowerShell run Command as different User when Credential Parameter is missing

I need to run a common PowerShell command to trigger a Group Policy Update "gpupdate" on a remote computer out of a workflow.
The workflow runs in a system user context, which do not have the local admin permissions on the clients to force a remote "gpupdate".
For that reason, I import a PowerShell credential secure string with "Import-CliXml" to run that statement in scope of a user which is local admin on the clients.
But, the command I want to use, don't support the native credential parameter. And I need to use a parameter for the remote client.
Invoke-GPUpdate -Computer $client -RandomDelayInMinutes 0
I tried many approches from the internet, but it won't work for me:
Start-Process powershell.exe -Credential $credentials -ArgumentList $ProcessCommand -WorkingDirectory $env:windir -NoNewWindow -PassThru
Start-Process powershell.exe -wait -Credential $credentials -ArgumentList "-command &{Start-Process Powershell.exe -argumentlist '$($cmnd)' -verb runas -wait}"
If I test to send the remote gpupdate out of a PowerShell console started with a user which is local admin on the remote client, it works.
Did anyone has a solution for this problem?
Many thanks!
When I connect to remote computers using PowerShell to execute commands on those computers I normally run the following. I've left an example of my code for you to use to execute Invoke-GPUpdate
#Local Host Computer
#$RequestingServer = $env:COMPUTERNAME
#Server List From Text File
#$ServerList = Get-Content 'C:\temp\servicetest\servers.txt'
#Server List In Script
$ServerList = 'Computer1','Computer2','Computer3','Computer4'
#Domain Admin Account
[STRING]$DomainAccountName = (whoami)
[STRING]$DomainAccountName = $DomainAccountName.Split("\")[1]
[STRING]$DomainAccountPassword = "Password01" #Obviously Change Password
$DomainAccountSecurePassword = $DomainAccountPassword | ConvertTo-SecureString -AsPlainText -Force
$DomainCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList $DomainAccountName, $DomainAccountSecurePassword
#Local Server Admin Account
[STRING] $LocalUser = "Administrator" #Obviously Change Account
[STRING] $LocalPassword = "Password01" #Obviously Change Password
$LocalSecurePassword = $LocalPassword | ConvertTo-SecureString -AsPlainText -Force
$LocalCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList $LocalUser, $LocalSecurePassword
#If running on multiple computers / servers etc. - - See Lines 5 and 8
ForEach($ComputerName in $ServerList) {
#Update Windows Something Locally - See Line 2
#$DomainSession = New-PSSession -Computername $RequestingServer -Credential $DomainCredentials
#Update Windows Something Remotely - See Lines 5 and 8
$DomainSession = New-PSSession -Computername $ComputerName -Credential $DomainCredentials
Invoke-Command -Session $DomainSession -ScriptBlock {
#Some commands need the computername currently using localhost...
$GPUpdateServer = $Using:ComputerName
#$GPUpdateServer = $Using:RequestingServer
# enter code of what you plan to do...
Invoke-GPUpdate -Computer $GPUpdateServer -RandomDelayInMinutes 0
}
} End of ForEach Statement
#If running on multiple computers / servers etc. - - See Lines 5 and 8
ForEach($ComputerName in $ServerList) {
#Update Windows Something Locally - See Line 2
#$LocalSession = New-PSSession -Computername $RequestingServer -Credential $LocalCredentials
#Update Windows Something Remotely - See Lines 5 and 8
$LocalSession = New-PSSession -Computername $ComputerName -Credential $LocalCredentials
Invoke-Command -Session $LocalSession -ScriptBlock {
#Some commands need the computername currently using localhost...
$GPUpdateServer = $Using:ComputerName
#$GPUpdateServer = $Using:RequestingServer
# enter code of what you plan to do...
Invoke-GPUpdate -Computer $GPUpdateServer -RandomDelayInMinutes 0
}
} End of ForEach Statement
Facing this problem more in detail, I tested the approach above with the remote PowerShell session. This needs some more preparation in domain for deploying all necessary GPO settings to all clients to make WinRM work.
The remote PowerShell approach works, but I found out that the Invoke-GPUpdate command is only available on clients which have RSAT installed. So only works on a few in clients in IT department.
$Session = New-PSSession -Computername $clientname -Credential $domainAccountWithLocalAdminRights
Invoke-Command -Session $Session -ScriptBlock { Invoke-GPUpdate -Computer $env:ComputerName -RandomDelayInMinutes 0 }
$Session | Remove-PSSession
I switched over to a different approach which worked for me without using remote PS sessions. Completely silent on the client, you will find the triggered gpupdates only in Windows event viewer.
Invoke-Command -ComputerName $clientname -ScriptBlock { gpupdate } -Credential $domainAccountWithLocalAdminRights

Run Get-ClusterGroup on remote server using Central server

I have a centralized server from which i can run the following PowerShell command to get the clustergroup of cluster servers.
Enter-pssession -computername (ip-address) -credential (domain user)
And it prompts me to enter password then i get the session and execute
get-clustergroup
Okay till this it is fine.
Now i wanted to make this fully automated by converting in to a PowerShell script
The following commands works well when i run it in Powershell ISE and gets me the output of get-clustergroup
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$user = "domain\user"
$cred = New-Object System.Management.Automation.PSCredential ($user,$password)
Enter-PSSession -ComputerName IP.Add.RE.SS -Credential $cred
get-clustergroup
but when i save the about script and run with PowerShell i get the following error.
get-clustergroup: the cluster service is not running
I want to automate the process by writing script to get get-clustergroup output of four cluster servers.
i am new to PowerShell scripting. how can i save the output?
Instead of creating a session to the other server, you can run the following which will run the command on the remote computer and return the output to your console:
Invoke-Command -ComputerName <IPAddress> -ScriptBlock { Get-ClusterGroup } -Credential $cred
You can store that output into a variable if you wish for future retrieval.
Since -ComputerName can accept an array object, you can modify your command to include all four of your servers. Below shows how to use all of your computer names and store the output in the variable $Output:
$Output = Invoke-Command -ComputerName "Server1","Server2","Server3","Server4" `
-ScriptBlock {Get-ClusterGroup} -Credential $cred
$Output
Your computer names could also be stored in a variable as an array. Then that variable can be used in your -ComputerName parameter:
$Computers = "Server1","Server2","Server3","Server4"
Invoke-Command -ComputerName $Computers -ScriptBlock { Get-ClusterGroup } -Credential $cred
See Invoke-Command for more information.

Enter-PSSession is not working in my Powershell script

When I run the lines below from a script the file ends up being created on my local machine.
$cred = Get-Credential domain\DanTest
Enter-PSSession -computerName xsappb01 -credential $cred
New-Item -type file c:\temp\blahxsappk02.txt
exit-pssession
When I run each line individually from the powershell console the remote session is created correctly and the file is created on the remote machine. Any thoughts on why? Is it a timing issue is the script perhaps?
Not sure if it is a timing issue. I suspect it's more like Enter-PSSession is invoking something like a nested prompt and your subsequent commands are not executing within it. Anyway, I believe Enter/Exit-PSSession is meant for interactive use - not scripting use. For scripts use New-PSSession and pass that session instance into Invoke-Command e.g.:
$cred = Get-Credential domain\DanTest
$s = New-PSSession -computerName xsappb01 -credential $cred
Invoke-Command -Session $s -Scriptblock {New-Item -type file c:\temp\blah.txt}
Remove-PSSession $s

PowerShell Remote sessions and scope question: Commands appear to run locally

Here's a sample script that attempts to create a remote session on a server, then use WMI to get a list of the server's IIS application pools, and list their names:
function Test-Remoting
{
[CmdletBinding()]
param
(
)
begin
{
Enter-PSSession TestServer
$appPools = Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsApplicationPool" -Authentication 6
$appPools | ForEach-Object {
$appPool = $_;
$appPool.Name
}
Exit-PSSession
}
}
This function is contained in a file called "Test-Remoting.ps1." I open up PowerShell, CD into the directory that contains this file, dot-source the file in, and call the function:
PS C:\Users\moskie> . .\Test-Remoting.ps1
PS C:\Users\moskie> Test-Remoting
But the result of this script is a list of the application pools on my local machine, and not TestServer.
Alternatively, if I run the following lines (identical to the ones in the function) manually at the PowerShell prompt, I do get the list of app pools on the remote server:
PS C:\Users\moskie> Enter-PSSession TestServer
[TestServer]: PS C:\> $appPools = Get-WmiObject -namespace "root\MicrosoftIISv2" -class "IIsApplicationPool" -Authentication 6
[TestServer]: PS C:\> $appPools | ForEach-Object { $appPool = $_; $appPools.Name }
<a list of the names of the application pools on TestServer>
[TestServer]: PS C:\>
I think there's a concept I'm oblivious to, regarding PowerShell remoting and scope. Can anyone help explain this behavior?
I believe Enter/Exit-PSSession is meant more interactive use. From the Enter-PSSession help:
SYNOPSIS
Starts an interactive session with a remote computer.
In a script, use New-PSSession and Invoke-Command like so:
$session = New-PSSession server01
Invoke-Command -Session $session {hostname}
Remove-PSSession -Session $session
Update: To execute a complete script remotely use the FilePath parameter on Invoke-Command:
icm server01 -FilePath C:\users\keith\myscript.ps1 -arg 1,2
This will copy the script to the remote computer server01 and execute it there with the supplied parameters.