On my target computer in PowerShell I run command
$FolderSize =(Get-ChildItem "C:\Users\JDoe" -force -Recurse -ErrorAction SilentlyContinue | Measure-Object length -sum).sum
and I get a value of 0.76 gb, which accurately corresponds to the compressed size of the folder on disk. However, when I try to run the command on a remote computer using
$folderSize = Invoke-Command -ComputerName "computername" {(Get-ChildItem -Path "C:\Users\JDoe" -Recurse -force -ErrorAction SilentlyContinue | Measure-Object -Property Length -sum).sum}
I get a different, MUCH larger number, 17 gb.
I tried running the first command in a pssession but still get the 17gb result. I also tried using
psexec \\\computername powershell "(Get-ChildItem "C:\Users\JDoe" -force -Recurse -ErrorAction SilentlyContinue | Measure-Object length -sum).sum" but still get the larger number.
I don't understand why the results obtained remotely are different than the actual size of the folder when I examine it locally. At least all the remote results are consistent, which tells me they are all measuring the same thing.
This is due to a junction in AppData\Local named Application Data that points back to AppData\Local
It appears that you can access this junction remotely (even from explorer using \\COMPUTER01\C$\Users\JDoe\AppData\Local\Application Data) so this is why you're getting different sizes, as it's recursively counting the same stuff up to the MAX_PATH limit.
Compare the following command's outputs on remote vs local:
Get-ChildItem 'C:\Users\JDoe\AppData\Local\Application Data' -Force
Local
PS C:\> Get-ChildItem 'C:\Users\JDoe\AppData\Local\Application Data' -Force
Get-ChildItem : Access to the path 'C:\Users\JDoe\AppData\Local\Application Data' is denied.
At line:1 char:1
+ Get-ChildItem 'C:\Users\JDoe\AppData\Local\Application Data' -Forc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\users\JDoe\A...plication Data\:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
Remote
PS C:\> Invoke-Command -ComputerName COMPUTER01 -ScriptBlock { Get-ChildItem 'C:\Users\JDoe\AppData\Local\Application Data' -Force }
Directory: C:\Users\JDoe\AppData\Local\Application Data
Mode LastWriteTime Length Name PSComputerName
---- ------------- ------ ---- --------------
d--hsl 4/16/2020 4:46 PM Application Data COMPUTER01
d----- 10/31/2019 9:43 AM ConnectedDevicesPlatform COMPUTER01
d----- 10/31/2019 9:52 AM ElevatedDiagnostics COMPUTER01
d----- 10/31/2019 9:43 AM Google COMPUTER01
d--hsl 4/16/2020 4:46 PM History COMPUTER01
d----- 4/16/2020 4:50 PM Microsoft COMPUTER01
d----- 9/16/2019 8:14 PM Microsoft Help COMPUTER01
d----- 10/31/2019 9:43 AM MicrosoftEdge COMPUTER01
d----- 10/31/2019 9:53 AM OpenShell COMPUTER01
d----- 4/16/2020 4:47 PM Packages COMPUTER01
d----- 10/31/2019 9:43 AM PlaceholderTileLogoFolder COMPUTER01
d----- 10/31/2019 9:43 AM Publishers COMPUTER01
d----- 3/18/2019 11:52 PM Temp COMPUTER01
d--hsl 4/16/2020 4:46 PM Temporary Internet Files COMPUTER01
d----- 10/31/2019 9:43 AM VirtualStore COMPUTER01
You will need to recurse separately from Get-ChildItem by using a recursive function like the one in this answer.
Thanks for all the suggestions! Ultimately, I opted to use Sysinternals "du" app and capture the output in a remote job, so as to minimize network traffic.
Thanks again!
CrookedJ's helpful answer provides the crucial pointer:
Due to the network-type logons that PowerShell remoting uses - see this GitHub issue - PowerShell code that runs remotely unexpectedly has the required privileges to recurse into the hidden system junctions. However, these hidden junctions exist solely for backward compatibility with pre-Vista Windows versions and are not meant to be traversed themselves: they simply redirect to the current locations of the well-known folders they represent.
E.g., the hidden "$HOME\My Documents" junction points to "$HOME\Documents" - see this article for background information.
Locally executing code - even if run as admin - is by design not allowed to access the contents of these hidden junctions.
When you use Get-ChildItem -Recurse:
Windows PowerShell reports access-denied errors while encountering these hidden junctions during recursive traversal, because it tries to recurse into them.
PowerShell [Core] v6+ more sensibly quietly skips these junctions during recursive traversal - both in local and remote execution, so your problem wouldn't arise. Generally, directory symlinks/junctions are not followed by default, unless -FollowSymlink is specified; even then, however, no error occurs - only a warning is emitted for each hidden junction, in recognition that the redirected-to real directories have already been traversed.
In Windows PowerShell, your remotely executing code therefore counts the files in certain directories (at least) twice - once as the content of the hidden junction, and again in the actual directory pointed to.
Therefore, there are two potential solutions:
If the target machine has PowerShell [Core] v6+ installed and remoting enabled, target it with your remoting command (which you can do even when calling from Windows PowerShell:
Simply add a -ConfigurationName PowerShell.<majorVersion> argument to your Invoke-Command call, e.g., -ConfigurationName PowerShell.7 for PowerShell [Core] 7.x versions.
Otherwise - if you must target Windows PowerShell - you need a workaround, which in your case is to use a custom Get-ChildItem variant that explicitly skips the hidden junctions during recursion:
# Note:
# * Hidden items other than the hidden junctions are invariably included.
# * (Other, non-system) directory reparse points are reported, but not recursed into.
# * Supports only a subset of Get-ChildItem functionality, notably NOT wildcard patterns
# and filters.
function Get-ChildItemExcludeHiddenJunctions {
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(ValueFromPipelineByPropertyName, Position = 0)] [Alias('lp', 'PSPath')]
[string] $LiteralPath,
[Parameter(ParameterSetName = 'DirsOnly')]
[switch] $Directory,
[Parameter(ParameterSetName = 'FilesOnly')]
[switch] $File,
[switch] $Recurse
)
# Get all child items except for the hidden junctions.
# Note: Due to the -Attributes filter, -Force is effectively implied.
# That is, hidden items other than hidden junctions are invariably included.
$htLitPathArg = if ($LiteralPath) { #{ LiteralPath = $LiteralPath } } else { #{ } }
$items = Get-ChildItem #htLitPathArg -Attributes !Directory, !Hidden, !System, !ReparsePoint
# Split into subdirs. and files.
$dirs, $files = $items.Where( { $_.PSIsContainer }, 'Split')
# Output the child items of interest on this level.
if (-not $File) { $dirs }
if (-not $Directory) { $files }
# Recurse on subdirs., if requested
if ($Recurse) {
$PSBoundParameters.Remove('LiteralPath')
foreach ($dir in $dirs) {
if ($dir.Target) { continue } # Don't recurse into (other, non-system) directory reparse points.
Get-ChildItemExcludeHiddenJunctions -LiteralPath $dir.FullName #PSBoundParameters
}
}
}
To use this function in a remote script block, you'll (also) have to define it there:
# Assuming the Get-ChildItemExcludeHiddenJunctions function is already defined as above:
# Get a string representation of its definition (function body).
$funcDef = "${function:Get-ChildItemExcludeHiddenJunctions}"
$folderSize = Invoke-Command -ComputerName "computername" {
# Define the Get-ChildItemExcludeHiddenJunctions function in the remote sesson.
${function:get-ChildItemExcludeHiddenJunctions} = $using:funcDef
(Get-ChildItemExcludeHiddenJunctions "C:\Users\JDoe" -Recurse -File |
Measure-Object -Property Length -Sum).Sum
}
I'm using my machine to run a script on the Domain Controller server using Enter-PSSession. It all works except, I can't save the outputs from the script on my local machine.
I want to save the outputs from the script as objects in my local machine in a csv format (Not on the Domain Controller server).
What I'm trying to do is save results from running commands like Get-ADDomainController etc..
Can someone please help me with this?
As for this …
I can't save the outputs from the script on my local machine.
… sure you can. Just create a log file as part of your session and copy that file back to your workstation for review, or just use the *-Transcript cmdlets to that creates a file automatically that you can copy over. The transcript will record everything that happens in the sessions.
Get-Command -Name '*transcript*' | ft -a
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Start-Transcript 3.0.0.0 Microsoft.PowerShell.Host
Cmdlet Stop-Transcript 3.0.0.0 Microsoft.PowerShell.Host
# get function / cmdlet details
(Get-Command -Name Start-Transcript).Parameters
Get-help -Name Start-Transcript -Full
Get-help -Name Start-Transcript -Online
Get-help -Name Start-Transcript -Examples
Or, don't use the interactive Enter-PSSession (explicit PowerShell Remoting) that puts you directly on the DC. Use a Implicit PSRemoting session, using New-PSSession and proxy the AD cmdlets to your machine for use.
$SessionAD = New-PSSession -ComputerName ''dc01.contoso.com
Invoke-Command $SessionAD -Command {Import-Module ActiveDirectory}
Import-PSSession $SessionAD -Module ActiveDirectory | Out-Null
$ADUSers = Get-ADuser -Filter *
$var = $ADUSers | Select-Object -Property Name, SamaccountName | Out-GridView -OutputMode Single
$GroupsMember = Get-ADUser -Filter ('Name -eq "' + $var.Name + '"') -Property MemberOf |
Select -ExpandProperty MemberOf |
Get-ADGroup -Property MemberOf |
Select Name
$GroupsMember
Get-PSSession | Remove-PSSession
Then you can run ADDS cmdlets as if they are actually on your machine and results are on your machine, or if you are on Window 8 or higher, just download and install (Win 7 - 8) / enable the RSAT tools (Win 10) directly and use them.
Remoting the Implicit Way
PowerShell Implicit Remoting: Never Install a Module Again
Also, take a look and Invoke-Command for running command locally or remotely.
I'm trying to script powershell to reach out to all AD Computers and pull a list of shares. I'm seeing some strange behavior with Get-WMIObject.
The script:
Import-Module ActiveDirectory
$Computers = Get-ADComputer -Filter * -SearchBase "Some OU" |
Where-Object {
$_.Name -match "-LT$" -or
$_.Name -match "-PC$" -or
$_.Name -match "-VPC$"
} |
Select-Object Name -ExpandProperty Name |
Sort
$Computers | ForEach-Object {
Write-Host $_
Write-Host "========================="
Get-WMIObject -Class Win32_Share -Computer $_
}
Normally, the output from the gwmi command looks like this:
Name Path Description
---- ---- -----------
ADMIN$ C:\Windows Remote Admin
C$ C:\ Default share
IPC$ Remote IPC
But instead, for that same computer I get this output:
...
[Computername]
=========================
Name Path Description
---- ---- -----------
ADMIN$ C:\WINDOWS Remote Admin
C$ C:\ Default share
IPC$ Remote IPC
print$ C:\WINDOWS\system32\spool\drivers Printer Drivers
ADMIN$ C:\WINDOWS Remote Admin
C$ C:\ Default share
IPC$ Remote IPC
print$ C:\WINDOWS\system32\spool\drivers Printer Drivers
ADMIN$ C:\Windows Remote Admin
C$ C:\ Default share
IPC$ Remote IPC
...
These two outputs are from the same computer. I've also output the computer name list, and I'm not missing a computer, so I don't think that they were combined.
Any clues?
You can use the following in Powershell to obtain the full path to where a specific process is running:
Get-Process | where{$_.Name -like "*iexplore*"} | Select Path
If I want to find this path for a service on a remote machine, I thought I could just utilise the following:
Get-Process -ComputerName $MyServer | where{$_.Name -like "*iexplore*"} | Select Path
However, this doesn't return anything. I can see that I can find the service itself with some details on current usage etc. but I cannot find the path for where the .exe file is located. (I also noticed I cannot see how many CPUs the process is using either).
Is there a way to find the path for the process?
Get-Process missing this, but you can use WMI:
Get-WmiObject -Class win32_process -ComputerName $MyServer -Filter 'name like "%iexplore%"' | select path
I want to get a list of processes under specific folder on some remote machine and kill them. However, if I add -ComputerName, Get-Process does not return Path as desired, thus I cannot Where with Path. Is there a way to Get-Process / Stop-Process on remote machine under a specific path?
// Paths are filled
PS C:\> Get-Process | Format-Table Name, Path
Name Path
---- ----
firefox C:\Program Files (x86)\Mozilla Firefox\firefox.exe
// Paths are empty
PS C:\> Get-Process -ComputerName localhost | Format-Table Name, Path
Name Path
---- ----
firefox
You could use Invoke-Command if Remoting is enabled on the remote server, and perform your action in the scriptblock of the command:
Invoke-Command -ComputerName remoteComputer -Script { param($pathFilter) Get-Process | ?{$_.Path -like $pathFilter} | Format-Table Name, Path } -Args "somefilter*"
I had a similar case that I resolved by using x64 version of powershell.