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.
Related
I want our Marketing deparment to extract all mailadressen from our users to CSV.
I created and Constrained Endpoint by using a configuration file, but i keep getting errors
First i created the config file:
New-PSSessionConfigurationFile -Path 'c:\marketing.pssc' -SessionType 'RestrictedRemoteServer' -LanguageMode FullLanguage -ModulesToImport ActiveDirectory -VisibleCmdlets ('Get-ADUser', 'Get-ADGroupMember', 'Export-Csv', 'Select-Object')
Register-PSSessionConfiguration –Name ‘Marketing’ -ShowSecurityDescriptorUI –Path ‘c:\marketing.pssc’
Im trying to run this code:
Set-ExecutionPolicy remotesigned -Scope Process -Force
$session = New-PSSession -ComputerName name -ConfigurationName 'marketing'
Invoke-Command -Session $session -Scriptblock {
Get-ADGroupMember -Identity "groupname" -Recursive | Get-ADUser -Properties Mail | where {$_.mail -ne $null} | Select Name,Mail | Export-CSV -Path "\\somepathto\file.csv" -NoTypeInformation
}
Remove-PSSession $session
i Expect an CSV file with name and email adressen.
The script works when running as a domain admin, currently i get this error:
The term 'where.exe' is not recognized as the name of a cmdlet, function, script file, or operable program....
According to the get-aduser commandlet, you should provide at least a required input param. Currently, it is stating the one you provide(-properties) is not approved by the validateset of the input param set.
https://learn.microsoft.com/en-us/powershell/module/addsadministration/get-aduser?view=win10-ps
This will help you out:
Get-ADUser -LDAPFilter '(mail=*marketing)' | select-object Name,Mail
https://ss64.com/ps/get-aduser.html
I'm looking to pop through a few webservers and import-module WebAdministration in order to change some recycling properties.
The issue is, it seems like foreach doesn't like being used for remote sessions. I'm guessing this is because a single block of code can only be executed in 1 remote session? Is there a way to pop through multiple sessions or do I have to do that by hand?
Just looking for some input on how to write a code to deploy to all my apppools on all my servers.
I've tested wildcarding the appPool directory and that seems to work.
IIS10
Powershell 5.1.14393.2189
$servers = #("MyEnvironment-web01","MyEnvironment-web02","MyEnvironment-web03")
foreach ($server in $servers) {
enter-Pssession -ComputerName $server
Write-Host $server
Read-Host -Prompt "Press Enter to continue" #This was added because I thought maybe it just needed time to connect? Doesn't need to be in here.
import-module WebAdministration
Get-ItemProperty -Path IIS:\AppPools\*
exit-PSSession
}
Lots of this:
import-module : The specified module 'WebAdministration' was not loaded because no valid module file was found in any module directory.
At line:1 char:1
Which is odd because that works just fine if I run the for-each loop manually. Except about 20% of the time when it doesn't....
I'm willing to bet I'm going about this ALL wrong.
$Cred = Get-Credential -UserName <domain\user> -Message 'Enter Password'
$servers = #("myserver1"."myserver2","etc")
foreach ($server in $servers) {
Invoke-Command -ComputerName $server -Credential $cred {
import-module WebAdministration
Get-ItemProperty -Path IIS:\AppPools\*
}
}
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
it runs the script up until the if clause (for running url) I cannot include the other url here but the script is
"welcome, want to go to site?"
$goyn=read-host -prompt "enter y or n"
if($goyn -eq 'y'){
start-process -Filepath chrome.exe -ArgumentList www.google.ca
}
else{"continue on your journey of awesomeness"}
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Get-WmiObject -Class Win32_OperatingSystem -ComputerName localhost |
Select-Object -Property CSName,#{n="Last Booted";
e={[Management.ManagementDateTimeConverter]::ToDateTime($_.LastBootUpTime)}}
for the start-process it throws the error "cannot find file specified followed by the path for $profile. wanted a quicker way of of getting to my work service page.
Specify full path to chrome.exe and other used items. E.g. C:\Program Files (x86)\Google\Application\chrome.exe. Adjust %PATH% otherwise. But I'd better specify full path.
So figured it out. it did not like start-process not one bit no matter how outrageously obvious I made the path. So I used the start cmdlet instead and put site in single quotes. Code looks like this:
"welcome, want to go to site?"
$goyn=read-host -prompt "enter y or n"
if($goyn -eq 'y'){
start 'www.google.ca'
}
else{"continue on your journey of awesomeness"}
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Get-WmiObject -Class Win32_OperatingSystem -ComputerName localhost |
Select-Object -Property CSName,#{n="Last Booted";
e={[Management.ManagementDateTimeConverter]::ToDateTime($_.LastBootUpTime)}}
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