Introduction
If you do some experimenting with PowerShell Remoting you might at one point ask yourself the following question: How do I get the my script file to the remote machine and kick it off via a PowerShell Remoting command. Of course you could copy it to a share on the remote computer, but that would require that somebody actually needs to create that share in advance. You could also consider the PModem approach that Oisin Grehan described in his blog post PowerShell 2.0 – Introducing the PModem File Transfer Protocol. This approach again requires that a script or module needs to be installed on the target machine.

Figure 1: A Star Trek transporter
The Transporter
Like I said. I played with several approaches and ended up with a fiendishly extreme solution. PowerShell Remoting lets you specify a Scriptblock that gets transported to the target machine. What if I would just dynamically generate this Scriptblock and have it contain the contents of a script file as a string. All I need on the target machine is to unload this cargo into a script file and call it. Here are the steps in detail:
- Read the file into a variable
- Encode the characters that the PowerShell compiler would not allow in a script block .
- Dynamically generate a code snippet with this encoded string.
- In this code snippet wrap the encoded cargo string into a few commands that would decode the string at the other end, save it as a file and execute it on the target machine.

Figure 2: Scotty is getting the plumbing right. Let’s hope for him and his 400 co-workers.
The Script
The transporter script
Set-StrictMode -Version "Latest"
$DebugPreference = "Continue"
$ScriptFileFolder = Split-Path -Path $($MyInvocation.MyCommand.Definition) -Parent
Write-Debug $ScriptFileFolder
#Use this function to create a value for the password in the configuration file
function Create-SecretPasswordFile([string] $PasswordFilePath)
{
$Password = Read-Host "Enter your password" -AsSecureString
$EncryptedPassword = ConvertFrom-SecureString $Password
$EncryptedPassword | Out-File -FilePath $PasswordFilePath
}
function Load-Configuration([string] $ConfigurationFileName)
{
$ConfigFilePath = Join-Path -Path $ScriptFileFolder -ChildPath $ConfigurationFileName
Write-Debug $ConfigFilePath
if(Test-Path -Path $ConfigFilePath)
{
. $ConfigFilePath
}
else
{
Throw "Configuration file `"$ConfigFilePath`" not found!"
}
}
function Create-PSRemotingSession()
{
$SecurePassWord = ConvertTo-SecureString $Config.PSRemotingCredentialPassword #Key is only usable by the domain user who created it
$DomainUser = "{0}\{1}" -f $Config.PSRemotingCredentialDomain, $Config.PSRemotingCredentialUserName
Write-Debug $DomainUser
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $DomainUser, $SecurePassWord
$Session = New-PSSession -ComputerName $Config.RemoteComputerName -credential $Cred
return $Session
}
function Encode-String([string] $s)
{
$s = $s.Replace('@', '[at]')
$s = $s.Replace("'", '[tick]')
return $s
}
function Copy-FileRemote([String] $LocalFilePathHere, [String] $LocalFilePathOnRemote="C:\Testing\Target.txt")
{
$ContentArray = Get-Content -Path $LocalFilePathHere -Encoding "UTF8"
foreach($Content in $ContentArray)
{
$CopyScriptStringOld = @'
Add-Content -Value '{0}' -Path "{1}" -Encoding "UTF8" -Force
'@
$CopyScriptString = @'
$Content = '{0}'
$Content = $Content.Replace('[at]', '@')
$Content = $Content.Replace('[tick]', "'")
Add-Content -Value $Content -Path "{1}" -Encoding "UTF8" -Force
'@
Write-Debug $Content
$EncondedContent = Encode-String -s $Content
$CopyScriptString = $CopyScriptString -f $EncondedContent, $LocalFilePathOnRemote
$CopyScript = [scriptblock]::Create($CopyScriptString)
Write-Debug $CopyScriptString
$job = Invoke-Command -Session $Session -Scriptblock $CopyScript -AsJob
$Null = Wait-Job -Job $Job
}
}
$Config = Load-Configuration -ConfigurationFileName "TransporterConfig.ps1"
$Session = Create-PSRemotingSession
Copy-FileRemote -LocalFilePathHere "C:\Users\KlausG\Desktop\Transporter\BeamMeUp-Scotty.ps1" -LocalFilePathOnRemote "C:\Users\Administrator\Desktop\Transporter\BeamMeUp-Scotty.ps1"
Copy-FileRemote -LocalFilePathHere "C:\Users\KlausG\Desktop\Transporter\Hello-World.ps1" -LocalFilePathOnRemote "C:\Users\Administrator\Desktop\Transporter\Hello-World.ps1"
$job = Invoke-Command -Session $Session -Scriptblock {. C:\Users\Administrator\Desktop\Transporter\Hello-World.ps1} -AsJob
$Null = Wait-Job -Job $Job
Remove-PSSession -Session $Session
The transporter configuration
Write-Debug "Loading Config File"
$Config = @{}
$Config.RemoteComputerName = "WIN-VE0uioD41"
$Config.PSRemotingCredentialDomain="Village"
$Config.PSRemotingCredentialUserName="Sepp"
#The password key is only usable by the domain user who created it.
#Use the Create-SecretPasswordFile function to create one for your login
$Config.PSRemotingCredentialPassword="0000000000000999999999...eeeeeeee"
return $Config
The hello world script
Set-content -Value "Hello World" -Path "C:\Users\Administrator\Desktop\Transporter\Hello-World.txt"
Power
A few things to consider before you push the power button to avoid the overloading of the circuits:
- Enable PS Remoting on both machines.
- Enable script execution on both machines.
- Make sure that both machines are on the same domain or, using winrm.cmd, create a trust relationship between the two machines.
These two blog posts describe the extra steps for running PowerShell Remoting in a workgroup setting:
http://blogs.msdn.com/b/wmi/archive/2009/07/24/powershell-remoting-between-two-workgroup-machines.aspx
http://rkeithhill.wordpress.com/2009/05/02/powershell-v2-remoting-on-workgroup-joined-computers-%E2%80%93-yes-it-can-be-done/

Figure 3: Before the transport

Figure 4: After the transport and the execution of hello-world.ps1
Download
The script files can be downloaded here: Transporter.zip
Ausblick
With great PowerShell comes great responsibility. May the Force be with you!