I am working on a class, which has to run both locally and remote. A simplified version is in the code below.
Assuming that:
The module file C:\Test\MyEmployee.psm1 looks like this:
using namespace System.Collections.Generic
class MyEmployee
{
[int]$UniqueID
[string]$Firstname
[string]$Lastname
[string]$Folder
MyEmployee( [int]$ID, [string]$First, [string]$Last, [string]$Folder )
{
$this.UniqueID = $ID
$this.Firstname = $First
$this.Lastname = $Last
If( $Folder.Length -lt 0 )
{
$this.Folder = $Folder
}
Else
{
$this.Folder = "Employee" + $this.UniqueID.ToString( ).PadLeft( 5 , "0" )
}
}
}
class MyEmployees
{
[List[MyEmployee]]$Employees
MyEmployees( )
{
$this.Employees = [List[MyEmployee]]::New( )
}
}
The calling script looks like this:
using Module "C:\Test\MyEmployee.psm1"
Function RunCode( )
{
$MyEmps = [MyEmployees]::New( )
$MyEmp = [MyEmployee]::New( 1, "John", "Doe", "" )
$MyEmps.Employees.Add( $MyEmp )
ForEach( $MyEmp in $MyEmps.Employees )
{
Write-Host "Employee $( $MyEmp.UniqueID ) : $( $MyEmp.Lastname ), $( $MyEmp.Firstname ) "
}
}
$ScriptBlock = {
Import-Module "C:\Test\MyEmployee.psm1"
$MyEmps = [MyEmployees]::New( )
$MyEmp = [MyEmployee]::New( 1, "John", "Doe", "" )
$MyEmps.Employees.Add( $MyEmp )
ForEach( $MyEmp in $MyEmps.Employees )
{
Write-Host "Employee $( $MyEmp.UniqueID ) : $( $MyEmp.Lastname ), $( $MyEmp.Firstname ) "
}
}
Clear-Host
$LocalHost = $Env:ComputerName
$Servername = $LocalHost
#$Servername = "remote"
If( $Servername -eq $LocalHost )
{
RunCode
}
Else
{
$LogResult = $( Invoke-Command -ComputerName $ServerName -ScriptBlock $ScriptBlock )
}
When run locally, both on "remote" or on my machine, the code executes perfectly, I get the expected result:
Employee 1 : Doe, John
Employee 2 : Doe, Peter
Employee 3 : Doe, Jane
Employee 4 : Doe, Petra
However, as soon as I un-comment the #$Servername = "remote"
line, telling PowerShell to run the ScriptBlock instead of te Function, I get the following error:
Unable to find type [MyEmployees].
+ CategoryInfo : InvalidOperation: (MyEmployees:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
+ PSComputerName : remote
What am I doing wrong here? Is it even possible to run class code remote like this in PowerShell?
Import-Module
doesn't make public the PowerShell classes defined in the psm1
, you'll need to use using module
in your $scriptblock
in the same way as you're doing in your calling script.
If you're creating a project that has only classes and no functions defined, then using a psm1
is already the incorrect approach, it would be much easier to have a ps1
that you can dot source.
You could essentially just do:
$ScriptBlock = {
. 'C:\Test\MyEmployee.ps1' # <= dot sourcing a ps1 makes all defined classes available in this scope
$MyEmps = [MyEmployees]::New( )
$MyEmp = [MyEmployee]::New( 1, 'John', 'Doe', '' )
$MyEmps.Employees.Add( $MyEmp )
ForEach ( $MyEmp in $MyEmps.Employees ) {
Write-Host "Employee $( $MyEmp.UniqueID ) : $( $MyEmp.Lastname ), $( $MyEmp.Firstname ) "
}
}
The next issue if you try to add the using module
statement in your $scriptblock
will be a parsing error because "A 'using' statement must appear before any other statements in a script." so to workaround that you will need to define your code as a string and then create a scriptblock from it using ScriptBlock.Create
.
In summary:
$ScriptBlock = [scriptblock]::Create(@'
using module "C:\Test\MyEmployee.psm1"
$MyEmps = [MyEmployees]::New( )
$MyEmp = [MyEmployee]::New( 1, 'John', 'Doe', '' )
$MyEmps.Employees.Add( $MyEmp )
ForEach ( $MyEmp in $MyEmps.Employees ) {
Write-Host "Employee $( $MyEmp.UniqueID ) : $( $MyEmp.Lastname ), $( $MyEmp.Firstname ) "
}
'@)
Then the Invoke-Command
statement should work properly assuming C:\Test\MyEmployee.psm1
exists in $ServerName
.