Powershell using Invoke-Command and Get-Childitem not delivering content - powershell

I am using Powershell 4.0 on a remote computer (rem_comp) to access another one (loc_comp; Powershell 2.0 installed here) in order to get the number of files listed without folders:
$var1 = 'H:\scripts'
Invoke-Command -Computername loc_comp -scriptblock {(Get-Childitem $var1 -recurse | Where-Object {!$_.PSIsContainer}).count}
However when using $var1 inside -scriptblock, it does not deliver anything (neither any error message).
When using
$var1 = 'H:\scripts'
Invoke-Command -Computername loc_comp -scriptblock {(Get-Childitem $ -recurse | Where-Object {!$_.PSIsContainer}).count}
it works!
Note: Changing var1 from ' to " does not help.
Running the command without Invoke-Command locally faces the same problem.
How to fix this?

To complement CmdrTchort's helpful answer:
PS v3 introduced the special using: scope, which allows direct use of local variables in script blocks sent to remote machines (e.g., $using:var1).
This should work for you, because the machine you're running Invoke-Command on has v4.
$var1 = 'H:\scripts'
Invoke-Command -Computername loc_comp -scriptblock `
{ (Get-Childitem $using:var1 -recurse | Where-Object {!$_.PSIsContainer}).count }
Note that using: only works when Invoke-Command actually targets a remote machine.

When you're using Invoke-command and a script-block , the scriptblock cannot access your params from the outer scope (scoping rules).
You can however, define the params and pass them along with the -Argumentlist
Example:
Invoke-Command -ComputerName "localhost" {param($Param1=$False, $Param2=$False) Write-host "$param1 $param2" } -ArgumentList $False,$True
The following should work for your example:
Invoke-Command -Computername loc_comp -scriptblock {param($var1)(Get-Childitem $var1 -recurse | Where-Object {!$_.PSIsContainer}).count} -ArgumentList $var1

Related

Powershell Get-Childitem in ISE

please can you help me.
I'm missing some information.
Why when I execute the code:
Invoke-Command -ComputerName domainpc -ScriptBlock {Get-ChildItem -path C:\Users -Filter "username1"}
the result is: username1
And when I execute this script:
$user = Read-Host "Please enter username"
Invoke-Command -ComputerName domainpc -ScriptBlock {Get-ChildItem -path C:\Users -Filter "$user"}
the result is a list of folder contained in C:\Users
I don't understand why executing script get different results than execute code without variable.
Seems that the problem is the variable.
Please can you explain this?
Thanks.
You are hitting scope "problems".
The variable $user inside your script block -ScriptBlock is not known.
Take a look here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_variables?view=powershell-7.1#using-local-variables
Use $Using:user to get around.

Get-Item cmdlet on remote fileshare not returning result with invoke-command

I run a simple Get-Item cmdlet to get the below result :
(Get-Item -Path \\priam\coste\ZEDMB5T-Intransit-report\* -Include DAILYREPORT_TES_MSS_20190426_*.XLS).name
I try the same using an invoke-command, but it does not give me anything :
invoke-command -ScriptBlock {(Get-Item -Path \\priam\coste\ZEDMB5T-Intransit-report\* -Include DAILYREPORT_TES_MSS_20190426_*.XLS).name} -computername localhost -Credential Get-Credential
There is nothing wrong with the credentials since it does work for the below command :
invoke-command -ScriptBlock {"testing"} -computername localhost -Credential Get-Credential
I have access to the path as well :
Get-Acl -Path \\priam\coste\ZEDMB5T-Intransit-report | Format-List -Property *
I am running the powershell ISE as an administrator, as seen in the title bar :
I am using a recent version of PS :
What am i doing wrong here?

PowerShell Invoke-Command severe performance issues

I'm having a heck of a time running a script on a remote system efficiently. When run locally the command takes 20 seconds. When run using Invoke-Command the command takes 10 or 15 minutes - even when the "remote" computer is my local machine.
Can someone explain to me the difference between these two commands and why Invoke-Command takes SO much longer?
Run locally on MACHINE:get-childitem C:\ -Filter *.pst -Recurse -Force -ErrorAction SilentlyContinue
Run remotely on \MACHINE (behaves the same rather MACHINE is my local machine or a remote machine: invoke-command -ComputerName MACHINE -ScriptBlock {get-childitem C:\ -Filter *.pst -Recurse -Force -ErrorAction SilentlyContinue}
Note: The command returns 5 file objects
EDIT: I think part of the problem may be reparse points. When run locally get-childitem (and DIR /a-l) do not follow junction points. When run remotely they do, even if I use the -attributes !ReparsePoint switch)
EDIT2: Yet, if I run the command invoke-command -ComputerName MACHINE -ScriptBlock {get-childitem C:\ -Attributes !ReparsePoint -Force -ErrorAction SilentlyContinue} I don't see the Junction Points (i.e. Documents and Settings). So, it is clear that both DIR /a-l and get-childitem -attributes !ReparsePoint do not prevent it from recursing in to a reparse point. Instead it appears to only filter the actual entry itself.
Thanks a bunch!
It appears the issue is the Reparse Points. For some reason, access is denied to reparse points (like Documents and Settings) when the command is run locally. Once the command is run remotely, both DIR and Get-ChildItem will recurse into reparse points.
Using the -Attributes !ReparsePoint for get-childitem and the /a-l switch for DIR does not prevent this. Instead, it appears those switches only prevent the reparse point from appearing in the file listing output, but it does not prevent the command from recursing into those folders.
Instead I had to write a recursive script and do the directory recursion myself. It's a little bit slower on my machine. Instead of around 20 seconds locally, it took about 1 minute. Remotely it took closer to 2 minutes.
Here is the code I used:
EDIT: With all the problems with PowerShell 2.0, PowerShell remoting, and memory usage of the original code, I had to update my code as shown below.
function RecurseFolder($path) {
$files=#()
$directory = #(get-childitem $path -Force -ErrorAction SilentlyContinue | Select FullName,Attributes | Where-Object {$_.Attributes -like "*directory*" -and $_.Attributes -notlike "*reparsepoint*"})
foreach ($folder in $directory) { $files+=#(RecurseFolder($folder.FullName)) }
$files+=#(get-childitem $path -Filter "*.pst" -Force -ErrorAction SilentlyContinue | Where-Object {$_.Attributes -notlike "*directory*" -and $_.Attributes -notlike "*reparsepoint*"})
$files
}
If it's a large directory structure and you just need the full path names of the files you should be able to speed that up considerably by using to the legacy dir command instead of Get-ChildItem:
invoke-command -ComputerName MACHINE -ScriptBlock {cmd /c dir c:\*.pst /s /b /a-d /a-l}
Try using a remote session:
$yourLoginName = 'loginname'
$server = 'tagetserver'
$t = New-PSSession $server -Authentication CredSSP -Credential (Get-Credential $yourLoginName)
cls
"$(get-date) - Start"
$r = Invoke-Command -Session $t -ScriptBlock{[System.IO.Directory]::EnumerateFiles('c:\','*.pst','AllDirectories')}
"$(get-date) - Finish"
I faced the same problem.
It is obvious that Powershell has problems with the transfer of arrays through PSRemoting.
It demonstrates this little experiment (UPDATED):
$session = New-PSSession localhost
$arrayLen = 1024000
Measure-Command{
Invoke-Command -Session $session -ScriptBlock {
Write-Host ("Preparing test array {0} elements length..." -f $using:arrayLen)
$global:result = [Byte[]]::new($using:arrayLen)
[System.Random]::new().NextBytes($result)
}
} |% {Write-Host ("Completed in {0} sec`n" -f $_.TotalSeconds)}
Measure-Command{
Invoke-Command -Session $session -ScriptBlock {
Write-Host ("Transfer array ({0})" -f $using:arrayLen)
return $result
} | Out-Null
} |% {Write-Host ("Completed in {0} sec`n" -f $_.TotalSeconds)}
Measure-Command{
Invoke-Command -Session $session -ScriptBlock {
Write-Host ("Transfer same array nested in a single object")
return #{array = $result}
}
} |% {Write-Host ("Completed in {0} sec`n" -f $_.TotalSeconds)}
And my output (time in ms):
Preparing test array 1024000 elements length...
Completed in 0.0211385 sec
Transfer array (1024000)
Completed in 48.0192142 sec
Transfer same array nested in a single object
Completed in 0.0990711 sec
As you can see, the array is tranfered more than a minute.
Single objects transfered in a few seconds despite their size

Run Registry File Remotely with PowerShell

I'm using the following script to run test.reg on multiple remote systems:
$computers = Get-Content computers.txt
Invoke-Command -ComputerName $computers -ScriptBlock {
regedit /i /s "\\SERVER\C$\RegistryFiles\test.reg"
}
The script doesn't error, but the registry entry doesn't import on any of the systems.
I know test.reg file is a valid registry file because I copied it over, ran it manually, and the registry key imports. I also made sure PowerShell Remoting is enabled on the remote computers.
Any ideas why the registry key isn't importing?
I found the best way not to mess with issues related to server authentication and cut down on complexity just to pass Reg file as parameter to function.
$regFile = #"
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters]
"MaxUserPort"=dword:00005000
"TcpTimedWaitDelay"=dword:0000001e
"#
Invoke-Command -ComputerName computerName -ScriptBlock {param($regFile) $regFile | out-file $env:temp\a.reg;
reg.exe import $env:temp\a.reg } -ArgumentList $regFile
I posted on some PowerShell forums and finally got this working.
I had to 1) move the $newfile variable inside the loop and 2) comment out the $ in the path stored in the $newfile variable.
For reference, the final script looks like this if anyone wants to use it:
$servers = Get-Content servers.txt
$HostedRegFile = "C:\Scripts\RegistryFiles\test.reg"
foreach ($server in $servers)
{
$newfile = "\\$server\c`$\Downloads\RegistryFiles\test.reg"
New-Item -ErrorAction SilentlyContinue -ItemType directory -Path \\$server\C$\Downloads\RegistryFiles
Copy-Item $HostedRegFile -Destination $newfile
Invoke-Command -ComputerName $server -ScriptBlock {
Start-Process -filepath "C:\windows\regedit.exe" -argumentlist "/s C:\Downloads\RegistryFiles\test.reg"
}
}

Get IIS Bindings from many webservers using powershell remoting, invoke command and module webadministration

Right now I made a small script to see my actual webbindings on a server.
Import-Module WebAdministration;
$today = Get-Date -format M.d.yyyy
$machine = hostname
function IISRport
{
Get-ChildItem -path IIS:\Sites | out-file "\\server01\d$\_utils\PowerShell Scripts\IISexport\IISExport$machine$today.txt"
}
IISReport
This wil outfile my IIS bindings to txt which works brilliant.
I now need to invoke that command to several other servers and save the output file on one server. since the machine name will be diffrent I expect as many outputs as servers.
I tried for example the following :
icm -comp server02 {get-childitem -path IIS:\Sites}
But than the imported Module webadministration is not working, since I only loaded on one server. So I tried to load that remotely using :
icm -comp server02 {import-module webadministration}
without success.
How to achieve that?
This will get the data from all machines defined in $machines and output to \\server01\d$_utils\PowerShell Scripts\IISexport\.
$today = Get-Date -format M.d.yyyy
$machines = #("server01","server02")
foreach($machine in $machines) {
icm -comp $machine { param($machine,$today)
import-module webadministration;
Get-ChildItem -path IIS:\Sites |
out-file "\\server01\d$\_utils\PowerShell Scripts\IISexport\IISExport$machine$today.txt"
} -ArgumentList $machine,$today
}
You can of course change the output path( and import list of machines from a file,AD or some other source).