Looking to run Invoke-GPUPdate -force to a group of remote computers and respond to the logoff prompt with "No".
Tried:
Echo "n" | invoke-gpupdate
Error:Invoke-gpupdate does not accept pipeline input
Command Used:
Invoke-GPUpdate -Computer $computer -RandomDelayInMinutes 0 -force
Unfortunately it looks like this cmdlet initiates/schedules a run of gpupdate that ends up happening separately (out of process), so there isn't much to do via PowerShell's standard ways of dealing with something like that, since the prompt doesn't come from within PowerShell. There's a -LogOff parameter, but it's a switch parameter which implies that its value is meant to be used just for doing the logoff. You can try it this way: -Logoff:$false but most likely it won't work to get rid of the prompt.
I think your best chance is not to use this cmdlet, but to instead use Invoke-Command with gpupdate.exe directly:
Invoke-Command -ComputerName $computer -ScriptBlock {
echo nn | gpupdate.exe /force
}
But this requires that PowerShell remoting is enabled on the machines you want to manage.
Related
how to obtain modules that a process running on a remote computer has loaded using PowerShell.
There are some process modules that I am getting error for, like permission denied and cannot enumerate. How can i address these error.
Cannot enumerate the modules of the "services" process.+
CategoryInfo : PermissionDenied: (System.Diagnostics.Process
(services):Process) [Get-Process], ProcessCommandException+
FullyQualifiedErrorId :
CouldnotEnumerateModules,Microsoft.PowerShell.Comands.GetProcessCommand
+ PSComputerName
using command:
$Module = Invoke-Command -Session $session -ScriptBlock { Get-Process -Module }
The problem isn't specific to remoting: there are processes whose modules fundamentally[1] cannot be enumerated due to lack of permissions, even when running with elevation (as admin).
Since the errors report are non-terminating errors, you can simply ignore the (all) errors with -ErrorAction Ignore at the source:
Invoke-Command -Session $session -ScriptBlock { Get-Process -Module -ErrorAction Ignore }
If you want to capture the error messages, so you can determine the processes for which enumeration failed, use -ErrorVariable errs -ErrorAction SilentlyContinue, which silences the errors, but collects them in variable $errs for later inspection:
Invoke-Command -Session $session -ScriptBlock { Get-Process -Module } -ErrorVariable errs -ErrorAction SilentlyContinue
Specifically, $errs.TargetObject will list the process objects whose modules couldn't be enumerated.
If the intent is to find (and possibly kill) all processes that use a module (DLL) of interest, use the standard tasklist.exe utility or taskkill.exe utility with the /m parameter.
$moduleOfInterest = 'oleaut32.dll' # example DLL
$pids = (tasklist /m $moduleOfInterest /fo csv | ConvertFrom-Csv).PID
# Alternatively, use `taskkill` in lieu of `tasklist` above in
# order to kill the processes directly.
if ($pids) { Stop-Process -Id $pids -WhatIf }
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf, once you're sure the operation will do what you want.
However, with this approach you may run into permission issues as well.
Note that tasklist.exe and taskkill.exe have a built-in remoting feature via parameter /S, allowing a single remote machine to be targeted.
However, using PowerShell's general-purpose remoting (via Invoke-Commands -ComputerName / -Session parameters) gives you more flexibility, notably the ability to target multiple computers in parallel.
[1] At least by default, elevation alone isn't enough; if anyone knows if there's a way to enumerate nonetheless, do let us know. This related question asks for that information specifically.
I'm writing a script which needs to RDP to a few servers, do processes there and then come back.
mstsc /v:<computer> by itself looks great as it's security/credential prompt is the same as if you manually executed it.
However, after some research it appears that's meant to be a command line utility and nothing more because trying things like:
mstsc /v:104.209.198.181 | Invoke-Command -ScriptBlock {"New-Item C:\Users\<me>\Desktop\Success.txt -ItemType file"}
doesn't work.
So I tried Enter-PSSession <computer> -Credential $env:UserName which people use but it looks like a mess to deal with compared to mstsc because it looks primitive (an article I read yesterday tried to say this type of prompt is ALWAYS a phishing scam which obviously it's not but try telling management), it doesn't auto-populate domains, and I get a WinRM error which I'm sure will be a rabbit hole.
So is it possible to RDP with mstsc and then pipe commands to it so they're executed on that computer?
The answer is no. You cannot initiate some kind of pipe using MSTSC.exe.
You can, however, use PSRemoting to send the command like you're trying to do already:
Invoke-Command -ComputerName '<FQDN>' -ScriptBlock {
New-Item -Path "$HOME\Desktop\Success.txt" -ItemType File
}
If you don't know the FQDN, then look up the IP using DNS:
[System.Net.Dns]::GetHostEntry('104.209.198.181')
All this failing.. you can fall back on WMI, but you don't get any console feedback:
$WmiArgs = #{
'Class' = 'Win32_Process'
'Name' = 'Create'
'ArgumentList' = 'powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command "New-Item -Path $HOME\Desktop\Success.txt -ItemType File"'
'ComputerName' = '104.209.198.181'
}
Invoke-WmiMethod #WmiArgs
While I tested the above is working, you can shorthand even this!
([wmiclass]'\\104.209.198.181\root\cimv2:win32_process').
Create('powershell -NoP -NonI -W Hidden -C "New-Item -Path $HOME\Desktop\Success.txt -ItemType File"')
With this method, however, you cannot pass credentials.
I have searched here and elsewhere and could not find an answer, so here is my question.
What is the best way to determine PowerShell version on a remote computer using WMI?
Background
My task is to audit and update some 1000 servers to recent version of PowerShell. Some of them have PowerShell v1 and some do not have WinRM configured, so Invoke-command is not an option. PSExec is also not an option in this environment. This is why I need to use WMI for this task.
Any help would be appreciated.
EDIT:
After much research I'm still leaning towards WMI. In particular retrieving the file version of "powershell.exe". This seems to me to be the only way to cover all versions.
Code I have so far is here:
$path = "C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"
$query = "SELECT Version FROM CIM_DataFile WHERE Name = '$path'"
$PSFileVer = Get-WmiObject -Query $query -ComputerName $servername -Credential $creds
$BuildVer = [version]$PSFileVer.Version.Split()[0]
All I need now is a comprehensive list, mapping file version (build number) to powershell version. Any Ideas?
The solution from here is
$command = "ipconfig/all > C:\temp\result.txt"
$cmd = "cmd /c $command"
Invoke-WmiMethod -class Win32_process -name Create -ArgumentList $cmd -ComputerName "remotepc"
sleep 1
Get-Content \\remotepc\C$\temp\result.txt
You can also do similar via this method. If you do so, you can't get the return value directly, u must redirect to a file.
You could setup share for the results instead of putting result on local machine
If it helps here is the command :
$command = "powershell -command "+'"$PSVersionTable.PsVersion.Major | out-file c:\temp\version.txt"'
I am wondering which is best practice considering both examples will probably work. Using the built in help examples I have written a script to install windows features on remote servers. Here is my code:
$servers = ('server1', 'server2', 'server3', 'server4')
ForEach ($server in $servers) {
Install-WindowsFeature -Name Desktop-Experience -ComputerName $server -IncludeAllSubFeature -IncludeManagementTools -Restart
}
Would the above be preferred OR should I wrap the "Install-WindowsFeature ..." in an "Invoke-Command" block like the following?
Invoke-Command -ComputerName server1, server2, server3, server4 -command {
Install-WindowsFeature -Name Desktop-Experience -ComputerName $server -IncludeAllSubFeature -IncludeManagementTools -Restart
}
Thanks for your insight!
Personally I would use the latter (directly call Install-WindowsFeature -ComputerName $server rather than do a separate Invoke-Command) in this case for the following reasons:
You may be hard-coding the feature names now, but in the future you may want to put those in a variable. If you put them in a variable, you'll have to pass it as a parameter into the Invoke-Command's script block. This is entirely possible, but more work.
By using your own loop, you can write progress messages, logging, etc.
You gain nothing by using Invoke-Command in this case because you're running a single command on the remote computer (as opposed to running multiple commands with -ComputerName parameters vs. running multiple commands inside the script block).
Here is what I have so far:
$source1="C:\Folder\Files\IPList.txt"
Get-Content $source1 |
Where-Object {-not(gwmi win32_process -ComputerName $_ -filter "name='Program.exe'")} |
Foreach-Object {Invoke-Command -ComputerName $_ -ScriptBlock {"C:\Program Files\Folder\Folder\Program.exe"}}
When I run this in ISE everything comes back as normal and says it has run correctly. However, when I look at my remote machine nothing has been executed. The Process Program.exe is not running and there for the exe should be launched. I am running this from a server to hit about 50 remote machines. Once it goes through all 50, I will loop it and have it do it again, then continue the process in an infinite loop.
What am I missing for the program to start remotely? By the way I am running this script on Server 2008 R2 and it is hitting Windows 7 machines.
Edit
I am wondering since I can see the process firing off, is this an issue with Windows 7? I know Microsoft changed things and a service cannot fire off an application in the User space. Do you think this would be part of the same problem?
Try adding the call (&) operator to the ScriptBlock:
$source1="C:\Folder\Files\IPList.txt"
Get-Content $source1 |
Where-Object {-not(gwmi win32_process -ComputerName $_ -filter "name='Program.exe'")} |
Foreach-Object {Invoke-Command -ComputerName $_ -ScriptBlock {& "C:\Program Files\Folder\Folder\Program.exe"}}
Here's a good article on the various methods available to you: http://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx
In your current syntax, the command your passing is just a string! This is what is happening on the remote end:
PS C:\> "C:\Program Files\Console2\Console.exe"
C:\Program Files\Console2\Console.exe
Powershell is echoing your string!
I am going to quote Oliver Lipkau as mentioned here: Source
If you need to start a process on a remote computer that keeps running after the script finished, use this function:
Function New-Process ([string]$computername, [string]$name) {
([WMICLASS]"\\$computername\ROOT\CIMV2:win32_process").Create($name)
}