PowerShell: string variable ignored by Get-ChildItem - powershell

Below PowerShell script should measure folder sizes on remote computers but apparently my $Desktop variable value is somehow ignored by Get-ChildItem and I get 0.00 MB. But when I replace $Desktop with explicit string i.e. "C:\Users\user1\Desktop" it works alright and I get e.g. 10.MB. Am I doing something wrong?
$file1="C:\computers_users.csv"
import-csv $file1 | ForEach-Object{
$Desktop = "C:\Users\$($_.user)\Desktop"
Invoke-Command -ComputerName $_.computer -ScriptBlock {
$FldSize =(Get-ChildItem $Desktop -recurse | Measure-Object -property length -sum)
"{0:N2}" -f ($FldSize.sum / 1MB) + " MB"}
}

try this:
Invoke-Command -ComputerName $_.computer -ScriptBlock {
$FldSize =(Get-ChildItem $args[0] -recurse | Measure-Object -property length -sum)
"{0:N2}" -f ($FldSize.sum / 1MB) + " MB"} -argumentlist $desktop
You need to pass the argument with -argumentlist because the invoke-command create a new powershell session not aware of calling session variables.

On a side note, in PowerShell 3.0 you'll be can use $using to pass local variables and to the remote machine:
Invoke-Command ... -ScriptBlock { $FldSize =(Get-ChildItem $using:Desktop ...

Related

Powershell retrieving cert by Thumbprint as string versus string variable

I'm trying to piece together some PowerShell code to loop through a list of servers, return some info regarding their IIS sites and bindings, and if they have an https binding, get the certificateHash and use that locate the cert by thumbprint and return its expiration date.
The problem I am having is, when i run my code below $binding.cerficateHash seems to return what I would expect, a string of the cert Hash, but when I use that certificateHash property to try and get the cert by its thumbprint, it doesnt work... but when I take the raw string value of the certificateHash value and hardcode it, it works...
I've inspected the certificateHash.GetType() and it appears to be just a string, so i dont understand what im doing wrong, and ive tried a handful of things, with no avail, granted this is my first crack at powershell so there's lots I don't know.
$sites = Invoke-Command -ComputerName $serverName { Import-Module WebAdministration; Get-ChildItem -path IIS:\Sites } -ErrorAction SilentlyContinue
foreach($site in $sites)
{
$serverName
$site.name
$site.physicalPath
foreach($binding in $site.bindings.Collection)
{
$binding.protocol
$binding.bindingInformation
$binding.certificateHash
$binding.certificateStoreName
if($binding.certificateHash)
{
# This outputs AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$binding.certificateHash
# this retrieves a cert and returns its expiration date, Woohooo!
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" })[0].GetExpirationDateString() }
# this does not find a cert, and ive tried many things, and no dice.
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq $binding.certificateHash })[0].GetExpirationDateString() }
# i've tried extracting the hash via "tostring" and using that, no dice
$hash = $binding.certificateHash.ToString()
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq $hash })[0].GetExpirationDateString() }
# i've tried adding some wildcards and using the -like operator, no dice.
$hash = "*" + $binding.certificateHash + "*"
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -lilke $hash })[0].GetExpirationDateString() }
}
}
}
Example output for a site.
Site1
D:\Apps\site1
http
*:80:Site1-test.ourdomain.com
https
*:443:Site1-test.ourdomain.com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
WebHosting
The computer you invoke the script block on doesn't know about the $binding variable in your local session. (That's also why it works when passing a literal string.)
Try passing the value as argument:
Invoke-Command -Computer $serverName -Script {
param ($hash)
(gci Cert:\LocalMachine\WebHosting | ? Thumbprint -eq $hash)[0].GetExpirationDateString()
} -Arg $binding.certificateHash

Powershell : build dynamic invoke-command for SSL and NOSSL connections

We have servers that are setup with type type of WinRM connection Secure and normal.
I need to establish a remote powershell connection with or without the -usessl switch.
I wanted to avoid having two identical script blocks with the only difference being having or not having the -usessl switch.
I tried using argument splatting but can't deal with the -usessl switch, also tried using invoke-expression but that broke the ability to retrieve data; Hash value from the remote job.
Any suggestions ?
if (test-wsman -ErrorAction SilentlyContinue -cn "$($Cmpname.name).sample.com" –UseSSL ) {
Write-host $Cmpname.name,WSMan Connected SSL
$strWSMAN = "SSL"
$strRemoteHash = invoke-command -cn "$($Cmpname.name).sample.com" -usessl -scriptblock {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
} else {
$strWSMAN = "NoSSL"
$strRemoteHash = invoke-command -cn "$($Cmpname.name).sample.com" -scriptblock {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
}
As mjolinor said, you need to provide a boolean value for a switch parameter when splatting:
$ParameterValues = #{
ComputerName = "$($Cmpname.name).sample.com"
UseSSL = [bool](Test-WSMan -ErrorAction SilentlyContinue -ComputerName "$($Cmpname.name).sample.com" –UseSSL)
ScriptBlock = {
Write-host "calculating Hash values"
$strLocalhash = Get-ChildItem -Path "c:\Windows\ccmcache" -Filter "Windows10.0-KB4041691-x64.cab" -Recurse -ErrorAction SilentlyContinue -Force | Get-FileHash -Algorithm sha1
New-Object pscustomobject -Property #{
RemotePath = $strLocalhash.path
RemoteHash = $strLocalhash.hash
}
}
}
Invoke-Command #ParameterValues

CPU & Memory Usage Script

Hi I am running this script again multiple servers (initially posted here) & I would like to get each specific servers names to be appeared in the result. But right now, I am able to get with the heading CPU & Memory Usage & then the usage for each server one after the other. Pls let me know how to get each server name & the result.
$Output = 'C:\temp\Result.txt'
$ServerList = Get-Content 'C:\temp\ServerNames.txt'
$ScriptBLock = {
$CPUPercent = #{
Label = 'CPUUsed'
Expression = {
$SecsUsed = (New-Timespan -Start $_.StartTime).TotalSeconds
[Math]::Round($_.CPU * 10 / $SecsUsed)
}
}
$MemUsage = #{
Label ='RAM(MB)'
Expression = {
[Math]::Round(($_.WS / 1MB),2)
}
}
Get-Process | Select-Object -Property Name, CPU, $CPUPercent, $MemUsage,
Description |
Sort-Object -Property CPUUsed -Descending |
Select-Object -First 15 | Format-Table -AutoSize
}
foreach ($ServerNames in $ServerList) {
Invoke-Command -ComputerName $ServerNames {Write-Output "CPU & Memory Usage"}
| Out-File $Output -Append
Invoke-Command -ScriptBlock $ScriptBLock -ComputerName $ServerNames |
Out-File $Output -Append
I see you're running with a loop on the servers names ($ServerNames is each server for each iteration), so why don't you use:
"Working on $ServerNames.." | Out-File $Output -Append
on the first line after the "foreach" statement?
Anyway, I think you can change your script like this:
On the script block add:
Write-Output "CPU & Memory Usage"
hostname | out-file $output -Append # this will throw the server name
Once you have this on the Scriptblock, you can run it like this:
Invoke-Command -ScriptBlock $ScriptBLock -ComputerName $ServerList
($ServerList is the original servers' array, which "Invoke-Command" knows how to handle).

Powershell passing arguments in ScriptBlock

I'm trying to get the last write time on a file from a remote server.
This doesn not work:
$server = "MyServerName"
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\$args[0]\hot.war" } -argumentlist $server | select -Property LastWriteTime
This does work:
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\MyServerName\hot.war" } -argumentlist $server | select -Property LastWriteTime
Can anyone help make the first set work?
Be careful with variables in strings: "\\$args[0]\hot.war" will be expanded to \\MyServerName[0]\hot.war.
Use "\\$($args[0])\hot.war" to be sure that $args[0] will be treated as a single expression.
See: http://blogs.msdn.com/b/powershell/archive/2006/07/15/variable-expansion-in-strings-and-herestrings.aspx
Another way, if you are using PowerShell 3. You can do something like this:
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {
Get-ChildItem "\\$using:server\hot.war"
} | select -Property LastWriteTime
You will want to add the server variable into your first line...
$server = "MyServerName"
$lastWrite = Invoke-Command -Computername $server -ScriptBlock {Get-ChildItem "\\$server\hot.war" } -argumentlist $server | select -Property LastWriteTime

Powershell Use Invoke-Command Session to Get-Childitem sizes on a remote computer

I am trying to use powershell to remotely access a computer and get the size of each sub-directory of a folder.
I am using the script to get each of the folder sizes and it works successfully:
$log = "F:\logfile.txt"
$startFolder = "C:\"
$colItems = Get-ChildItem $startFolder | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object
foreach ($i in $colItems){
$itemSum = Get-ChildItem ("$startFolder\" + $i.Name) -recurse | Measure-Object -property length -sum
"$startFolder\$i -- " + "{0:N2}" -f ($itemSum.sum / 1MB) + " MB" >> $log
}
This is how I tried to incorporate it using the Invoke-Command and it is yielding no results.
#login info
$username = "domain\user"
$password = 'password'
$log = "C:\logfile.txt"
$startFolder = "comp-name\e"
#setup login credentials
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$session = new-pssession "comp-name" -Credential $cred
Invoke-Command -Session $session -ScriptBlock {$colItems = Get-ChildItem $startFolder | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object
foreach ($i in $colItems){
$itemSum = Get-ChildItem ("$startFolder\" + $i.Name) -recurse | Measure-Object -property length -sum
"$startFolder\$i -- " + "{0:N2}" -f ($itemSum.sum / 1GB) + " GB" >> $log
}
}
I have already done the step of enabling ps-remoting on both computers.
Thanks in advance for the help!
The variable $startFolder needs to be passed into the scriptblock e.g.:
Invoke-Command -Session $session -ScriptBlock {param($path,$log)
$colItems = Get-ChildItem $path | Where-Object {$_.PSIsContainer} | Sort-Object
foreach ($i in $colItems) {
$itemSum = Get-ChildItem "$path\$($i.Name)" -recurse | Measure-Object -property length -sum
"$startFolder\$i -- " + "{0:N2}" -f ($itemSum.sum / 1GB) + " GB" >> $log
}
} -Arg $startFolder,$logFilePath
I was able to get this working (no redirect to log file):
23# Invoke-Command acme -ScriptBlock {param($path)
>>> $colItems = Get-ChildItem $path | Where-Object {$_.PSIsContainer} | Sort-Object
>>> foreach ($i in $colItems) {
>>> $itemSum = Get-ChildItem $i.FullName -recurse | Measure-Object -property length -sum
>>> "$startFolder\$i -- " + "{0:N2}" -f ($itemSum.sum / 1MB) + " MB"
>>> }
>>> } -Arg c:\bin
>>>
\Orca -- 3.63 MB
\Reg -- 0.06 MB
\Src -- 0.25 MB
\sym -- 19.09 MB
\x64 -- 0.71 MB