How to Access Custom PowerShell 5.0 Classes from a separate ps1 file - powershell

I created a class in a ps1 file that works just fine IN the ps1 file itself. I can run various tests with the class and the tests in the same file.
My trouble is I don't seem to be able to find a way to put my Class in one file and the Class Usage code in another.
With functions, you can just dot source them to bring any external ps1 files into your current script. It looks like Classes for Powershell do not work this way.
How can I organize code to keep classes in separate files from the executing script?
Do I have to use modules? How do I do that?

In the file Hello.psm1:
class Hello {
# properties
[string]$person
# Default constructor
Hello(){}
# Constructor
Hello(
[string]$m
){
$this.person=$m
}
# method
[string]Greetings(){
return "Hello {0}" -f $this.person
}
}
In the file main.ps1:
using module .\Hello.psm1
$h = New-Object -TypeName Hello
echo $h.Greetings()
#$hh = [Hello]::new("John")
$hh = New-Object -TypeName Hello -ArgumentList #("Mickey")
echo $hh.Greetings()
And running .\main.ps1:
Hello
Hello Mickey

This may not apply to your case, but I have had nightmares with PowerShell caching information (in my case it was just for modules). Things I'd changed in the module weren't being properly loaded - even after a reboot.
I found deleting the cache stored in a subdirectory of C:\Users\<YourUsername>\AppData\Local\Microsoft\Windows\PowerShell solved my issue.

The simplest solution is to use the using keyword, at the very beginning of your script :
using module .\path\to\your\Module.psm1
Path can be relative from your current file.
To prevent caching problem, use powershell.exe .\script.ps1 to execute it, even in the PoweShell ISE console.

Related

Powershell module function not visible in Get-Modules

I have a simple powershell module containing a single function, an abridged version of this is as follows:
function My-Func
{
.
.
.
}
Export-ModuleMember -Function 'My-Func'
My manifest file contain a line to explicitly export this:
FunctionsToExport = "My-Func"
Everything uploads to the powershell gallery via Publish-Module without any problems, then when I come to install this and run Get-Module, I do not see the function in the export commands column of the output, also I when I attempt to call the function powershell tells me it does not exist.
I have a psm1 file for my module and a psd1 manifest, for some reason when I only see Manifest as the module type, I'm guessing I need to see script ?.
Any ideas ?
in your .psm1:
remove the Exported-ModuleMember line
in your .psd1:
RootModule = 'yourmodule.psm1'
FunctionsToExport = #('function1','function2')
the psd1 file FunctionsToExport works like the Exported-ModuleMember command. it's a cleaner way to define things from one centralized file.

Properly referencing scripts in PowerShell

I'm a newbie in PowerShell and I'm having some problems when defining some utility scripts that are "included" in other files; the problem is regarding paths. Let me explain the issue with a simple example:
Imagine you have some utility script named utility.ps1 located under /tools and you want to invoke it from a build.ps1 placed under /build. Now imagine that the utility.ps1 invokes some other utility script in the same folder called "utility2.ps1". So,
utility.ps1:
[...]
.".\utility2.ps1"
[...]
build.ps1:
[...]
."..\tools\utility.ps1"
[...]
The problem here is that when calling from build to utility.ps1 and then from this last one to utility2.ps1, powershell is trying to load utility2.ps1 from the current dir (build) instead of the tools dir.
My current workaround is pushd . before calling and popd after that but it smells bad, I know; on the other hand if some day I move scripts to some other location I'd need to update all my scripts that use the moved one... a mess.
So, I'm doing something very wrong; what would be the proper one to do this, making the client script unaware and independant of the "utility" script location?
The answer by PetSerAl in the comments is perfect but will work for PowerShell 3+. If for some reason you want your script to be backward compatible as well, then use the below code snippet to find the $ScriptRootPath (which is automatically populated via $PSScriptRoot for PS 3+)
$ScriptRootPath = Split-Path $MyInvocation.MyCommand.Path -Parent
For PS 3+, populate this variable via the global variable:
$ScriptRootPath = $PSScriptRoot
and then use the solution as described by PetSerAl,
i.e. for utility.ps1: . "$ScriptRootPath\utility2.ps1"
for build.ps1: . "$ScriptRootPath\..\tools\utility.ps1"
Also refer this question on SO

PowerShell Syntax Error

I am working on a powershell script that will create TFS build definitions. I have used below example as my starting point.
http://geekswithblogs.net/jakob/archive/2010/04/26/creating-a-build-definition-using-the-tfs-2010-api.aspx
I have the script done in powershell and it creates me a build definition file in TFS. One thing I am stuck in is creating Process information such as "Item to build" and "Projects to build". The C# code for this is given below
//Set process parameters
varprocess = WorkflowHelpers.DeserializeProcessParameters(buildDefinition.ProcessParameters);
//Set BuildSettings properties
BuildSettings settings = newBuildSettings();
settings.ProjectsToBuild = newStringList("$/pathToProject/project.sln");
settings.PlatformConfigurations = newPlatformConfigurationList();
settings.PlatformConfigurations.Add(newPlatformConfiguration("Any CPU", "Debug"));
process.Add("BuildSettings", settings);
buildDefinition.ProcessParameters = WorkflowHelpers.SerializeProcessParameters(process);
Below is the powershell code I have written to achive above.
Write-Host"Set process parameters "$now
$process=[Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::DeserializeProcessParameters($def.ProcessParameters)
Write-Host"Set build settings properties "$now
$settings=new-object-`enter code here`TypeNameMicrosoft.TeamFoundation.Build.Workflow.Activities.BuildSettings
$sList=New-Object-TypeNameMicrosoft.TeamFoundation.Build.Workflow.Activities.StringList
$sList="$/pathToProject/project.sln"
$settings.ProjectsToBuild =$sList
$process.Add("BuildSettings", $sList)
But the above segment of code does not create me the Build settings in my build definition file. Myquestion is am I doing this the correct way in powershell? I feel I am not writing the powershell code incorrectly as I am newbie to powershell. Any guidance and
help would be appreciated
Calling a constructor with parameters should be done like this in PowerShell:
$ns = 'Microsoft.TeamFoundation.Build.Workflow.Activities'
$settings.ProjectsToBuild = new-object "$ns.StringList" '$/pathToProject/project.sln'
Also note the use of single quotes around the TF server path. $ is s special character in PowerShell - tells it what follows is either a variable name or sub-expression even in a string. Unless that string is single quoted. In which case, PowerShell doesn't interpret any special characters within the string.

Powershell - Task Manager - Permissions?

I've wrote a script that reads all mail from an Exchange inbox and writes all sort of output (.txt files, folders, ..). Everything is working fine when I run this in the powershell ISE. The problem starts when I make a bat (powershell.exe C:\script.ps1) and schedule the bat in TaskManager. All folders are created, output files are created but they are empty. Normally, the content of a Global Variable goes into the file, but now it doesn't..
## Global ###
$body = ''
$dateReceived = ''
$attachCont = ''
Function check() {
DoSomething
$Global:body = $mail.body.text
}
Function Write() {
$body >> 'file.txt'
}
I'm doing something like this (look above). 'File.txt' is made, but it's empty. When I do a 'Write-Host $body' just above writing the file, I see nothing. So there is something wrong with the global variable (I think?). NOTE: When I run it in the ISE, the content is written, when I start the task in Task manager, the content ISN'T written.
Is this a permission problem/a global variable problem?
Got it. When I don't define the Global variables first, it works like a charm.

powershell: run code when importing module

I have developed a powershell module in C#, implemented a few commands.
How can I execute C# code in this module when it's imported by Powershell?
Create a module manifest with the ModuleToProcess (or RootModule in V3) field set to the PSM1 file and the NestedModules set to the DLL e.g.:
RootModule = 'Pscx.psm1'
NestedModules = 'Pscx.dll'
This is what we do in the PowerShell Community Extensions where we do the same thing - fire up a script first. You can see our PSD1 file here.
This is a very basic solution, simply replace code within the {} with your source. (my test below)
add-type 'public class c{public const string s="Hello World";}';[c]::s
enjoy
I'm also writing a binary cmdLet in .NET. I have found that if you create a class that inherits from at least DriveCmdletProvider, that class can implement InitializeDefaultDrives.
This method will get call when import-module is called on your DLL.
You could use this 'feature' to stand up some session (or module session) data.