I got a hashtable with connection-data for various (S)FTP-Connections. Thanks to the Module "WinSCP" I can easily create sessions for (S)FTP-Transfers
Currently I'm saving the Hastable-result in a temporary variable $arguments and use this variable for the splatting afterwards
Even if it's just one line of code that could be spared: is there a way to avoid the variable $arguments?
Sample-Hashtable:
$preferences = #{
"FTPUser" = #{
HostName = "ftp.domain.com"
PortNumber = 21
Protocol = "FTP"
}
"SFTPUser" = #{
HostName = "sftp.otherdomain.com"
GiveUpSecurityAndAcceptAnySshHostKey = $true
}
}
Function Get-FtpSession with the current temp-variable:
function Get-FtpSession ($user) {
$arguments = $preferences["$user"]
$session = New-WinSCPSession #arguments
return $session
}
I thought that I can use something like this (which does not work):
$session = New-WinSCPSession #($preferences["$user"])
P.S: I know that this question is kind of pointless but I was still wondering if it could be solved
If you are willing to modify that cmdlet, you can achieve something similar by adding ValueFromPipelineByPropertyName switch to it's parameters. Thus you can feed parameters from pipe.
function foo { [CmdletBinding()]param(
[parameter(ValueFromPipelineByPropertyName=$true)][String]$arg1,
[parameter(ValueFromPipelineByPropertyName=$true)][String]$arg2
)
Write-Host ($arg1 + " " + $arg2 + "!")
}
[pscustomobject]#{arg1="hello"; arg2="there"} | foo
Related
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
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
As a Powershell learner, I ran into -f format operator.
I then thought to myself, is it possible to dynamically change code based on a condition. Take for example a mapping drive scenario:
$creds = Get-Credential
$share_needs_creds = $true
$drive_map = New-Object -ComObject WScript.Network
if ($share_needs_creds){
$drive_map.MapNetworkDrive('Z:', '\\server\share', $false, "$($creds.Username)", $($creds.GetNetworkCredential().Password)")
} else {
$drive_map.MapNetworkDrive('Z:', '\\server\share', $false)
}
can this be re-written as follows:
$creds = Get-Credential
$share_needs_creds = $true
$drive_map = New-Object -ComObject WScript.Network
if ($share_needs_creds){
$condition_smart = ', "$($creds.Username)", $($creds.GetNetworkCredential().Password)"'
} else {
$condition_smart = ''
}
$drive_map.MapNetworkDrive('Z:', '\\server\share', $false{0}) -f $condition_smart
Any advise is appreciated!
That's not going to work - the -f operator returns exactly 1 string as output - and you need to pass a variable number of arguments to the parameter list of the method you're calling.
If you want to invoke a method dynamically on a ComObject, prepare your argument list as an array, and pass it to .Invoke() on the method name exposed by PowerShell's type adapter:
$arguments = #(
'Z:', '\\server\share', $false
if($share_needs_creds){
$creds.Username, $creds.GetNetworkCredential().Password
}
)
$drive_map.MapNetworkDrive.Invoke($arguments)
foreach ($Person in $People) {
$NewUserParams = #{
Name = $Person.Name
Server = 'xxxx.com:389'
Path = 'CN=Users,CN=addressBook,DC=xxxx,DC=com'
Credential = $Credentials
givenName = $Person.givenName
otherAttributes = #{sn=$Person.sn}
}
New-ADUser #NewUserParams
}
I have many additional attributes (otherAttributes) that I would like to add that are available to me in the formart New-ADUser -Name XXX -OtherAttributes #{sn=xxx} . However, I am trying to using splatting to make the OtherAttributes more readable, along with other required parameters. I don't need to using splatting for the entire command, my goal was to break up otherAttributes so it wasn't a long string that wrapped. Ideas?
The value of otherAttributes is just another hashtable, and can be wrapped like any other hashtable:
$NewUserParams = #{
'Name' = $Person.Name
'Server' = 'server.example.com:389'
'Path' = 'cn=Users,cn=addressBook,dc=example,dc=com'
'Credential' = $Credentials
'givenName' = $Person.givenName
'otherAttributes' = #{
'sn' = $Person.sn
}
}
Personally, I recommend putting the keys of the hashtables in quotes to avoid surprises, but at least in the above example that's not required.
If the above doesn't work for you you need to provide more details about the code you're running and the error(s) you're getting. Displaying the content of the generated hashtable inside the loop usually helps troubleshooting problems with particular values.
I am trying to figure out a way to pass an array to a named parameter in a separate script. However, I could not find any solution.
Test2.ps1:
param(
[int]$a,
[int]$b,
[int]$c,
[string[]]$d
)
write-host "`$a = $a"
write-host "`$b = $b"
write-host "`$c = $c"
write-host "`$d:" -nonewline
foreach($str in $d) {
write-host " $str" -nonewline
}
write-host
Main script:
$arr = #("abc", "def", "ghi")
$params = #{
a = 1;
b = 2;
c = 3;
d = $arr
}
#invoke-command -filepath "test2.ps1" -ArgumentList 1,2,3,#("abc", "def", "ghi")
$scriptPath = "test2.ps1"
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)")
invoke-command -scriptblock $sb
When executed, I got output that
$d:System.Object[]
The line below was copied from another Stackoverflow answer but I don't quite understand how it worked for the first 3 named parameters.
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)")
Especially the "$(&{$args} #params)" part. I have basic understanding of splatting, but this is beyond me. Much appreciated if someone can explain the syntax a bit for me.
When you put #params inside an expandable string, you're forcing the parser to convert the resulting output to a string, and the default behavior of ToString() (if not overridden) is to just return the type name of the object in question.
Simply wait until you call the script before you supply the parameters:
$sb = [scriptblock]::Create("$(get-content $ScriptPath -Raw)")
& $sb $args #params
or, if you want to dot-source the script with specific parameters:
$sb = [scriptblock]::Create("$(get-content $ScriptPath -Raw)")
& {.$sb $args #params}