Hi all, I have a script that does a couple of things, it’s primary function is to install software remotely and quietly
Prompts user to input machine name
Set-Location to network shared directory containing software
From that directory, Copy-Item to remote admin share
Invoke-Command msiexec.exe to quietly install software using local path to copied software
Remove-Item the copied software
Running the script, the file get successfully copied to their location. However when trying to Invoke-Command, the software does not install. On top of that, when trying to remove the file, error feeds back:
Cannot bind argument to parameter 'Path' because it is null.
+ CategoryInfo : InvalidData: (:) [Remove-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.RemoveItemC
ommand
I recognize that there’s something wrong with how I’ve stored the path in the variable, but I’ve had the script write the output of the install directory path and it looks fine. Also, if I do these step by step in the terminal, software successfully installs quietly. Here is my actual script:
$workingDirectory = $PSScriptRoot
$user = (whoami).Split('\')[1]
$remoteComputer = Read-Host "Please enter computer name"
$t2Profile = "\\computer\C$\Users\$user\installdirectory" # this uses admin share to copy files over
if (Test-Path $t2Profile){
if(-not(Test-Path "$t2Profile\installfolder")){
New-Item -ItemType Directory "$t2Profile\installfolder"
} else {
$atlas = "\\path\to\file\share\Mozilla Firefox\126.0" # file in \126.0 directory is called "Firefox Setup 126.0.msi"
$msiName = (Get-ChildItem $atlas).Name
Set-Location $atlas # set location to the network shared directory so that Copy-Item circumvents double-hop issues
Write-Output "Copying $atlas\$msiName to $remoteComputer"
Copy-Item -Path ".\*msi" -Destination "$t2Profile\installfolder" # Copy the .msi setup file to the \installfolder
Set-Location $workingDirectory
$remoteDirectory = "\\$remoteComputer\C$\Users\$user\installdirectory\$msiName"
$installDirectory = "C:\Users\$user\installdirectory\$msiName" # this is the variable I use for the path in invoke-command
if(Test-Path "$remoteDirectory"){
Invoke-Command -ComputerName $remoteComputer -ScriptBlock {msiexec.exe /i "$installDirectory" /qn} #invoke msiexec on the remote machine using path to copied .msi
Invoke-Command -ComputerName $remoteComputer -ScriptBlock {Remove-Item "$installDirectory"}
} else {
Write-Host "Something went wrong"
}
}
} else {
Write-Host "Profile has not been created on this machine. Please use 'Enter-PSSession <computer name>' to add your profile, then rerun this script."
}
If anyone has recommendations or ideas please let me know. Thank you!
I would say “can use the using scope modifier” versus “need to use” as there is another option. You can pass in the data with the -ArgumentList parameter using either automatic variables or explicit parameters.
In this example we passed in the $installDirectory value to the parameter installDirectory. It doesn’t have to be the same name and sometimes it’s less confusing to keep them different
In this example we still pass in the value, it just gets populated in the automatic $args variable since there is no parameter declaration. Due to you having the path quoted, we needed to use a subexpression $(...) to reference the first entry in args. Note that in this simple example with only one value passed in, $args by itself would’ve worked. However, if you pass more values, they are referenced by index. An example
Invoke-Command -ComputerName $remoteComputer -ScriptBlock {
Write-Host the first value passed in is $args[0]
Write-Host the second value passed in is $args[1]
} -ArgumentList 'First', 'Second'
Naturally, for the named parameters you’d specify the required variables matching the amount of arguments being passed in.
Invoke-Command -ComputerName $remoteComputer -ScriptBlock {
Param(
$First,
$Second
)
Write-Host the first value passed in is $First
Write-Host the second value passed in is $Second
} -ArgumentList 'First', 'Second'
Interesting, I appreciate the alternative suggestion. I didn’t realize Invoke-Command worked this way. Solution has already been found, but how do you feel about my path variables? Is there a more efficient way of defining these path variable? I feel like the script is bloated with variables, but I can’t think of a way to trim it down at the moment.
I have been teaching myself how to script, and would rather not pick up bad habits (and/or waste my time typing more than I need).
No, I thought the variables were fine. The one thing I meant to nitpick was multiple calls with invoke-command. You should put all commands in one invoke-command execution.