I'm using Invoke-Command, but this question can be relevant to any command using splat. I essentially want to pass two sets of variables that will be useful in the splat command, but I'm not sure how I can do this.
In the code below, the Invoke-Command successfully connects to both servers, but the output I get is "Server1 Info" from both servers, which makes sense since the code is reading it like I'm trying to pass two arguments to both servers and it is taking what is in the first argument and writing it to host. What I really want it to do though is only pass one argument each time and to move down the list of which argument is being passed as it connects to successive servers.
$ServerList = "Server1","Server2"
$ServerArgs = "Server1 Info","Server2 Info"
$SB = {
param($Arg1)
Write-Host $Arg1
}
$SplatInfo = #{
ComputerName = $ServerList
ArgumentList = $ServerArgs
}
Invoke-Command #SplatInfo -ScriptBlock $SB
You can only send one set of arguments per invocation of Invoke-Command.
If you want to pass different arguments per remote machine, you need to call Invoke-Command once per server:
$ServerList = "Server1","Server2"
$ServerArgs = "Server1 Info","Server2 Info"
$SB = {
param($Arg1)
Write-Host $Arg1
}
for($i = 0; $i -lt $ServerList.Count; $i++){
$SplatInfo = #{
ComputerName = $ServerList[$i]
ArgumentList = $ServerArgs[$i]
}
Invoke-Command #SplatInfo -ScriptBlock $SB
}
... in which case you might want to organize the input data slightly differently:
$inputList = #(
#{ ComputerName = "Server1"; ArgumentList = #("Server1 Info") }
#{ ComputerName = "Server2"; ArgumentList = #("Server2 Info") }
)
$baseSplat = #{
ScriptBlock = {
param($Arg1)
Write-Host $Arg1
}
}
foreach($entry in $inputList){
Invoke-Command #baseSplat #entry
}
Using the hashtable idea. This should run in parallel still.
import-csv file.csv |
% { $ServerArgs = #{} } { $ServerArgs[$_.server] = $_.args }
$ServerList = $ServerArgs | % keys
$SB = {
param($Arg1)
[pscustomobject]#{result = $Arg1[$env:computername]}
}
$SplatInfo = #{
ComputerName = $ServerList
ArgumentList = $ServerArgs
ScriptBlock = $SB
}
Invoke-Command #SplatInfo
result PSComputerName RunspaceId
------ -------------- ----------
Server1 Info Server1 gacbbb30-2492-41df-b181-9ebf6395b8b6
Server2 Info Server2 ebca4b52-c349-4ff7-972e-e67d07d9c0c3
Related
I'm developing a powershell script (and kind of new to it) to go to a couple of servers and extract the RDP logons, so we can check if a certain policy is being followed.
So I've search a bit and now I got the script output exatcly as I want. But now I want to send the result over email.
But I have a problem, because the variable which is output to console (with the info I need) is inside a Invoke-Command, so I cannot use it after outside the Invoke-Command to send the email.
This is my code:
$ServersToCheck = Get-Content "C:\Temp\Servers-RDP2.txt"
foreach ($server in $ServersToCheck) {
Invoke-Command -ComputerName $server -ErrorAction SilentlyContinue {
$username = "user"
$FilterPath = "<QueryList><Query Id='0'><Select Path='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'>*[System[(EventID=1149) and TimeCreated[timediff(#SystemTime) <= 604800000]]] and *[UserData[EventXML[#xmlns='Event_NS'][Param1='{0}']]]</Select></Query></QueryList>" -f $username
$RDPAuths = Get-WinEvent -ErrorAction SilentlyContinue -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' -FilterXPath $FilterPath
[xml[]]$xml = $RDPAuths | Foreach { $_.ToXml() }
$EventData = Foreach ($event in $xml.Event) {
New-Object PSObject -Property #{
TimeCreated = (Get-Date ($event.System.TimeCreated.SystemTime) -Format 'dd-MM-yyyy hh:mm:ss')
User = $event.UserData.EventXML.Param1
Domain = $event.UserData.EventXML.Param2
Client = $event.UserData.EventXML.Param3
Server = hostname
}
}
$EventData | FT
}
}
So, I need to use $EventData outside the Invoke-Command so I can add the results of all servers and then send it over by email.
How can I use that variable outside the Invoke-Command?
Thanks
I have written a below script which prompts user to enter a remote computer name, checks "HideFastUserSwitching" value in the registry of the computer and changes it if user wishes so.
The script does its job, I have tested to change the value from 1 to 0 on one machine. But if I run the script again and try to change it back to 1, it doesn't do that. The value stays 0.
Could you please help me figure out why does this happen? If you have remarks about other pieces of code in this script, you are welcome to give me some advise, it will be appreciated.
$PN = (Read-Host "Enter PN#").ToUpper().Trim()
$path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System"
$regKeyName = "HideFastUserSwitching"
$hideFastUserSwitchingValue = $null
Function getHideFastUserSwitchingValue {
$regKeyValue = $null
$parameters = #{
ComputerName = $PN
ScriptBlock = {
Param ($path, $regKeyName)
Get-ItemProperty -Path $path -Name $regKeyName | Select-Object -ExpandProperty $regKeyName
}
ArgumentList = $path, $regKeyName
}
Invoke-Command #parameters
}
Function setHideFastUserSwitchingValue($value) {
$parameters = #{
ComputerName = $PN
ScriptBlock = {
Param ($path, $regKeyName)
Set-ItemProperty -Path $path -Name $regKeyName -Value $value
}
ArgumentList = $path, $regKeyName
}
Invoke-Command #parameters
}
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
$yesNo = (Read-Host "HideFastUserSwitching value is $hideFastUserSwitchingValue. Do you want to modify it [y/n]?").ToUpper().Trim()
if($yesNo -eq "Y") {
$input = Read-Host "Enter the new value"
setHideFastUserSwitchingValue($input)
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
Write-Host "HideFastUserSwitching value is now $hideFastUserSwitchingValue"
} else {
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
Write-Host "HideFastUserSwitching value has not been modified and is equal to $hideFastUserSwitchingValue"
}
Quick one? - As I'm still learning Powershell. I was wondering if it was possible to combine parameters inside a script with parameters entered on the command line?
i.e. I have a function like this as an example...
function GetInfo {
param ($SiteName, $Subnet, $Cred, $ComputerName)
Write-Host "Checking $Site and $ComputerName"
<# ... Additional logic to check computername prefix & subnet etc. #>
}
$SiteName = "London1"
$Subnet= "192.168.10.1/24"
$Cred = <supplied>
#$ComputerName = "blah1"
GetInfo $SiteName $Subnet $Cred $ComputerName
$SiteName = "London2"
$Subnet= "192.168.11.1/24"
$Cred = <supplied>
#$ComputerName = "blah2"
GetInfo $SiteName $Subnet $Cred $ComputerName
Now say inside the script I would specify the SiteName, Subnet, Cred.... but on the command line I would like to specify -ComputerName
But as I'm using the script & let's say I know that Lon1-PC1 is in "London1" I would like to do this on the calling command:
.\GetPCInf.ps1 -ComputerName "Lon1-PC1"
or
.\GetPCInf.ps1 -ComputerName "Lon2-PC1"
Obviously there will be additional logic inside the script to say that if the -ComputerName prefix is "Lon1" then do X or if -ComputerName prefix is "Lon2" then do Y..
Obviously I know I can just put the computername in the script, save it & run it.
So far when I try, nothing happens in relation to the -Computername return..
I haven't yet tried combining a parameter & Args - but I've read Args is not the best to use so I'm trying to avoid it.
If this can't be done then fair enough, just wondered if someone might know if this can be done, as it saves me typing in:
.\GetPCInf.ps1 -SiteName London1 -Subnet "192.168.10.1/24" -Cred mycreds -ComputerName "Lon1-PC1"
each time I want to run it....
I suppose I could create batch files to call the script & put %1 for computername in the batch file & call it that way, but just curious really..
Many thanks.
So far when I try, nothing happens in relation to the -Computername return..
Scripts are just functions stored in files - and they support param blocks and parameter declarations just like functions - so declare a $ComputerName parameter:
# GetPCInf.ps1
param(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
# define GetInfo function
function GetInfo {
param ($SiteName, $Subnet, $Cred, $ComputerName)
Write-Host "Checking $Site and $ComputerName"
<# ... Additional logic to check computername prefix & subnet etc. #>
}
# define table of arguments to pass to GetInfo
$GetInfoArgs = #{
ComputerName = $ComputerName
}
# add additional arguments based on computer name value
if($ComputerName -like 'Lon1-*'){
$GetInfoArgs += #{
SiteName = "London1"
Subnet= "192.168.10.1/24"
Cred = $(<# credential goes here #>)
}
} elseif($ComputerName -like 'Lon2-*') {
$GetInfoArgs += #{
SiteName = "London2"
Subnet= "192.168.11.1/24"
Cred = $(<# credential goes here #>)
}
} else {
$GetInfoArgs += #{
SiteName = "DefaultSite"
Subnet= "192.168.12.1/24"
Cred = $(<# credential goes here #>)
}
}
# invoke GetInfo function
GetInfo #GetInfoArgs
Powershell script is only returning one value from Invoke-Command not all the objects I would expect.
I am adding a property to an existing object on a remote server then returning it back.
$inventory contains application objects that look like this:
Name : Centinel-Dev-AmazonServer
Platform : Centinel
Type : Windows Service
Tier : APP
Status : Running
Name : Portal-QA-Walmart
Platform : Portal
Type : Windows Service
Tier : APP
Status : Running
There are many more application objects similiar to the two given when I run my script it only returns the first object not all.
function verify_workingDir {
param($server)
$inventory = GetInventory -Server $server
$return_object = #()
$return_object += Invoke-Command -ComputerName $server -ScriptBlock {
$inventory = $args[0]
$return_object = #()
foreach ($Application in $inventory) {
$applicationParamters = Get-ItemProperty -Path x:\x\x\x\$($Application.Name)\Parameters
$verify_object = [pscustomobject] #{
WorkingDirectory = $applicationParamters.ServiceWorkingDir
}
$ExpandVerifyObject = $verify_object | Select-Object -Property #{
Name = "MyProperties"
Expression = {$_.WorkingDirectory }
} | Select-Object -ExpandProperty MyProperties
Add-Member -MemberType NoteProperty -Name WorkingDirectory -InputObject $Application -TypeName PSObject -Value $ExpandVerifyObject
$return_object += $Application
}
return $return_object
} -ArgumentList ($inventory) #| Select Name, Platform, Type, Tier, Status, ServerName, WorkingDirectory
return $return_object
}
Answer:
$inventory = $args[0] ---> $inventory = $args
Try $inventory = $args instead of $inventory = $args[0].
Passing arrays to script blocks is not the easiest thing because $args flattens everything to a single array. If you pass -ArgumentList #(1,2,3), 4, then $Args will be a single array #(1,2,3,4) and $Args[0] will be 1.
If you ever need to pass complex arguments with -ArgumentList, pass them all as a single HashTable: -ArgumentList #{FirstArg = #(1,2,3); SecondArg = 4}.
I am trying to figure out what I am doing wrong here. Very new to powershell so be gentle... Trying to run a PSSession on remote system (reading in from list of systems). Then trying to return the value for missing patches to my local system to then export to CSV. I am looking solely for a number to be returned. The value gets displayed in the Powershell window when inside the Invoke-command but then at the bottom of the script nothing is shown. Can anyone offer some advice how I can pass that value back to my system to then be able to export to a csv? Any advice would be greatly appreciated.
$array1 = Get-Content "C:\Users\******\Desktop\Server_List.txt"
$ReportResults = New-Object System.Collections.Generic.List[System.Object]
$Searchresult = #()
#parse thru each machine name in
foreach ($MachineName in $array1)
{
Write-host $MachineName
$session = New-PSSession -ComputerName $MachineName
Invoke-Command -Session $session {
Param($ReportResults)
#Get All Assigned updates in $SearchResult
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$SearchResult = $UpdateSearcher.Search("IsAssigned=1 and IsHidden=0 and
IsInstalled=0")
Write-Host "total=$($SearchResult.updates.count)"
$ReportResults.add($SearchResult)
} -ArgumentList $ReportResults
Remove-PSSession $session
}
$ReportResults # | export-csv C:\Users\******\Desktop\Compprogs\Test.csv -
Notypeinformation
You've to "mark" parameter as ref. See this link for further info.
Alternativelly you can return your desired value via Write-Output. Example:
$returnValue = Invoke-Command -ScriptBlock {
Write-Output "Hello World"
}
# $returnValue should include "Hello World"
Write-Host $returnValue
Be aware that when you use Write-Ouput multiple times at Invoke-Command $returnValue will include ALL values wrote to the output steram via Write-Ouput.
Hope that helps