How can I re-use/import script code in PowerShell scripts? - powershell

I have to create a PowerShell script which does exactly same thing as my previous script, but this time I have to read a CSV file instead of an XML file. My plan is to create a PowerShell script which has common functions required for both scripts and re-use this common script file in both main files.
Suppose I create 2 main files in 2 directories in C:\ drive and keep my common file and other 3rd party libraries in a folder of D:\ drive, e.g. C:\script_1_folder\Script1.ps1, C:\script_2_folder\Script2.ps1 and common file and 3rd party libraries will be in D:\script_common.
How do I call\re-use common file in my main files (how to get the path, do I have to create an instance of common file and how do I use it)
What is the difference between
$script_path = $myinvocation.invocationname;
$script_folder = split-path $script_path -parent;
write-host $script_folder
$script_name = split-path $script_path -leaf;
$current_folder = [system.io.directory]::getcurrentdirectory()
[system.io.directory]::setcurrentdirectory($script_folder)
Set-Location $script_folder
add-type -path ".\AlexFTPS-1.1.0\AlexPilotti.FTPS.Client.dll"
and
$path = (split-path $MyInvocation.MyCommand.Path)
$loggerPath = $path + "\Logger\release\Logger.ps1";
.$loggerPath;
$logger = Logger;
$logger.load($path + "\Logger\config\log4ps.xml","log.log");
and what is the best way to do it with regard to my problem?
How do I create a temp folder in windows temp folder?

Common Code In Powershell
You can just put the code you want to include in a different PS1 file, and then "dot source" that file to include it in the current scope:
. D:\script_common\MyCode.ps1
That's all there is to that.
Using a Module
You might consider using a module instead, which can be included using the Import-Module cmdlet. You might have used this to work with things like Active Directory, where you could do something like this:
Import-Module ActiveDirectory
In that case, you only need the name of the module because it's in a special directory.
To write your own modules in Powershell, you name the module with a .psm1 extension. Typically, you don't do free floating code in one of these; you write functions which are then available to the code which imports the module.
To import a script module from anywhere, use the full path:
Import-Module D:\script_common\MyModule.psm1
Module Paths
When you create your own modules, you can keep them any old place and then refer to them by their full path (as above). There are also several locations that Powershell looks for modules:
$PSHome\Modules (%Windir%\System32\WindowsPowerShell\v1.0\Modules) -- Reserved for modules that ship with Windows. Do not put things here.
$Home\Documents\WindowsPowerShell\Modules (%UserProfile%\Documents\WindowsPowerShell\Modules)
%ProgramFiles%\WindowsPowerShell\Modules -- this isn't mentioned in the link, and seems to be used more for Desired State Configuration modules (probably because it applies to the entire system).
These are defaults, but Powershell uses its own environment variable called PSModulePath to determine where to look, and much like PATH you can add your own folder(s) to that variable.
That lets you keep your modules in your own location. Do see the link for more info on how to structure your folders and how to do naming.
So as far as keeping your modules and "3rd party" modules in the same place, that depends on the 3rd party stuff. It may install its own modules in its own place and modify the path, or it may just let you put them wherever you want.
Creating a Temp Folder
You can use the TEMP or TMP environment variables to get the path of the temp folder. To retrieve them in Powershell, use $env:TEMP or $env:TMP.
You'll have to come up with a unique name of a folder to create in there. One way to do that might be to use a GUID:
$dirName = [System.Guid]::NewGuid().ToString()
New-Item -Path "$($env:TEMP)\$dirName"

You should be able to dot source the script like that:
. "C:\script_common\script.ps1"
after that you can use all the functions like they were in the script you are running.
But... the better way to do it would be to create a module with your common functions (How to here: Scripting Guy´s Blog. (TLDR Version: place functions into psm1 file, put into modulepath, import using Import-Module, profit)
For creating a folder:
New-Item C:\Temp\yourfolder -type directory

Here is my attempt to create a template system in powershell : https://github.com/kayasax/PowershellTemplate
It allows to reuse functions you save in the repository by using tags in the template
eg :
<include logging/log>
The content of the file log.ps1 found in the logging directory of the function repository will be inserted when tranforming template to script

Related

How can you run a powershell script if you do not know the location is will be run from?

I am new to powershell so this may just be me not understanding something.
I am writing a tool in powershell and have the tool working in c:\Users\Documents\WindowsPowerShell\Modules.
What I would like is the ability to copy the tool to a USB stick and run it from the USB stick.
How can I get the tool to run if its not in the PSModulePath environment variable path?
Or how can I add the USB stick added to the PSModulePath path?
Thanks for the help.
I have a solution that works by placing the following in a .ps1 script and using that to import the dependent modules and call the first function.
#find local directory
$LocalDir = Split-Path $MyInvocation.MyCommand.Path -Parent
#prove correct directory found.
Write-Host "Starting module load: $($MyInvocation.MyCommand) in directory = $LocalDir"
#load modules
Import-Module $LocalDir/Module –Force
#call first function
firstfunction
I could not get $PSScriptRoot to work but the Split-Path $MyInvocation.MyCommand.Path –Parent code has the advantage in that it also works in ISE.
I then placed the dependent modules in directories below the .ps1 script.
If you want your script to be portable, you will have to carry around the dependent module with you as well. However, in your script, you can make sure to add the full path to the module's parent directory on your USB to the PSModulePath as a process-level environment variable:
$portableModuleDirectory = [System.IO.DirectoryInfo]'./relative/path/to/dependent/module'
$env:PSModulePath += ";$(Split-Path -Parent $portableModuleDirectory.FullName)"
Import-Module ModuleName
However, this is cumbersome to have to do. The best solution here would be to host your own internal nuget feed to push your package to, so you can install the module on any machine you need it on from your network, if you can't upload it to the public PowerShell Gallery (which you should host your own feed/squid proxy for business needs).

What is the purpose of the *.psm1 files in a Powershell module?

So I implemented my first Powershell module with a bunch of .ps1 files (one per function) and the .psd1 manifest file.
I am trying to understand what is the purpose of the .psm1 files - do I need them at all in my module?
What is their added value?
EDIT 1
Here is my .psd1 file:
#{
ModuleVersion = "0.0.19106.59054"
GUID = "59bc8fa6-b480-4226-9bcc-ec243102f3cc"
Author = "..."
CompanyName = "..."
Copyright = "..."
Description = "..."
ScriptsToProcess = "vsts\config.ps1"
VariablesToExport = #(
"TfsInstanceUrl",
"TfsApiVersion",
"QANuGetRepoUrl"
)
NestedModules = #(
"db\Backup-Database.ps1",
...
"vsts\work\Get-WorkItems.ps1"
)
FunctionsToExport = #(
"Assert-ExtractionDestFolder",
...
"Write-HostIfNotVerbose"
)
PrivateData = #{
PSData = #{
ExternalModuleDependencies = "SqlServer"
}
}
}
Like I said, each function is in its own file.
what is the purpose of the .psm1 files - do I need them at all in my module?
In script modules, i.e., modules authored in PowerShell (as opposed to compiled binary cmdlets), it is only *.psm1 files that provide the module-specific behaviors distinct from regular *.ps1 script files (separate, isolated scope, private commands, control over exported commands).
Typically, a script module manifest has a RootModule entry pointing to (the main) *.psm1 file; for smaller modules it is not uncommon for all of the module's functionality to be implemented in this one *.psm1 file.
In fact, a stand-alone *.psm1 file can also act as a module, though it doesn't integrate with PowerShell's module auto-discovery and auto-loading feature.
Note that if you were to use a regular *.ps1 script directly in RootModule, its definitions would be loaded into the caller's scope, not the module's; that is, you would lose the benefits of a module.
Even though you're listing regular *.ps1 scripts in your NestedModules manifest entry, by virtue of using that specific entry these scripts are dot-sourced in the module's context and thereby become part of the module:
This is conceptually equivalent to creating and referencing a root *.psm1 script in RootModule, and - instead of defining a NestedModules entry - explicitly dot-sourcing your *.ps1 scripts from there - see bottom section.
Note that if you were to reference *.psm1 files in NestedModules, they would truly become nested modules, with their own scopes; a nested module is usable from the enclosing module, but not visible to the outside world (though you can list it among the loaded modules with Get-Module -All).
Listing *.ps1 files in NesteModules vs. dot-sourcing them from the RootModule
While there should be no difference in functionality, using a *.psm1 RootModule to dot-source the *.ps1 files containing your module's functions can potentially simplify things, if you simply need to dot-source all *.ps1 files located in your module directory's subtree:
# Add this to the *.psm1 file specified in 'RootModule'
# Find all *.ps1 files in the module's subtree and dot-source them
foreach ($script in
(Get-ChildItem -File -Recurse -LiteralPath $PSScriptRoot -Filter *.ps1)
) {
. $script
}
If you need to load the scripts in a specific order, need only a subset of the scripts, or want to speed things up slightly (though I doubt that the speed difference will be noticeable), you can dot-source the files individually from a RootModule *.psm1 file, as an alternative to listing them in the NestedModules entry:
# Add this to the *.psm1 file specified in 'RootModule'
# Dot-source the *.ps1 files individually.
. "$PSScriptRoot/db/Backup-Database.ps1"
# ...
. "$PSScriptRoot/vsts/work/Get-WorkItems.ps1"
Again, all of the above approaches are functionally equivalent.
Given that you explicitly export functions via the ExportedFunctions entry (as is advisable), the use of *.ps1 files that must be dot-sourced is ultimately an implementation detail that is not relevant for the purposes of command discovery and module auto-loading - it only matters at actual import time.
.psm1 file is powershellmodule file. When we create script module we write all the functions of a module in a .psm1 file then we export the functions and then we can use those functions by importing module.
.psm1 is basically refers the powershellmodule. Powershell directly identifies anything which is written in this file will be a part of module.

How can I set up a default powershell profile for all my co workers at work as simple as possible?

What do I want to achieve?
I have one ps1 file that has all of my functions inside. In the first step I want to convert it into a ps module. Then I want to have the following:
Colleague gets a script or bat he has to run ONCE. This will set his Modules Environment path $Env:PSModulePath to a path on a network drive everyone has access to
Copy and paste a custom profile.ps1 into the users %userprofile%\Documents\WindowsPowershell that imports the module
Every user should now have the powershell scripts I made available in their shell
How I tried to solve it
The way me and a colleague have set it up in the past is with this:
(we have a profile.ps1 file that does the following):
#set path for profile_loader.ps1
$path = "\\server\share\folderwithscripts";
#call profile_loader.ps1
. "$path"
Then this profile_loader.ps1 baiscally just loads tons of scripts (ps1 files) like this:
. "\\server\share\pathtoanotherscript.ps1
Line after line.
I don't like it and it is too complicated for my 25 other colleagues I want to set up in the future.
Question
What is the best way to achieve this? A good old .bat file that copy and past the ps1 file into their userprofile? Or is there a better way?
As someone who had their $profile wiped and set to a "company default", for the love of god, don't.
If you have to, then I suggest just creating a profile you want everyone to have with all your modules in a shared location, like your securely locked down Sysadmin folder.
Do psedit $proile.AllUsersAllHosts on your machine, modify that, then make a text file with all the hostnames you want to destroy with your own forced profile. Throw this in there to make it import your modules by default.
# Checks your server share for any PSM1 files, could change to include PS1 as well I suppose. Long name because its in a $Profile so ¯\_(ツ)_/¯
$ModulePathWithLongNameBecauseSomeoneMayUseThisInAnActualScript = Get-ChildItem -file -Recurse "\\server\share\" -Include "*.psm1"
# Sets module path for other adhoc module calls if they dont want to restart their Powershell
$env:PSModulePath = $env:PSModulePath + ";\\server\share\"
# Imports all PSM1 files from the ModulePath*
Foreach($psm in $ModulePathWithLongNameBecauseSomeoneMayUseThisInAnActualScript){
Import-Module "$($ModulePath.FullName)"
}
Run this on your machine to deliver your soul crushing $profile to your colleagues who may have had their own setup.
# Get a list of machines that your staff will use and throw them into a txt or csv etc.
$PCsForForcedProfile = Get-Content "\\server\share\PleaseNo.txt"
Foreach($Colleague in $PCsForForcedProfile){
Copy-Item "C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1" "\\$Colleague\C$\Windows\System32\WindowsPowerShell\v1.0\" -force
}

How to load my custom PowerShell module? [duplicate]

I have to create a PowerShell script which does exactly same thing as my previous script, but this time I have to read a CSV file instead of an XML file. My plan is to create a PowerShell script which has common functions required for both scripts and re-use this common script file in both main files.
Suppose I create 2 main files in 2 directories in C:\ drive and keep my common file and other 3rd party libraries in a folder of D:\ drive, e.g. C:\script_1_folder\Script1.ps1, C:\script_2_folder\Script2.ps1 and common file and 3rd party libraries will be in D:\script_common.
How do I call\re-use common file in my main files (how to get the path, do I have to create an instance of common file and how do I use it)
What is the difference between
$script_path = $myinvocation.invocationname;
$script_folder = split-path $script_path -parent;
write-host $script_folder
$script_name = split-path $script_path -leaf;
$current_folder = [system.io.directory]::getcurrentdirectory()
[system.io.directory]::setcurrentdirectory($script_folder)
Set-Location $script_folder
add-type -path ".\AlexFTPS-1.1.0\AlexPilotti.FTPS.Client.dll"
and
$path = (split-path $MyInvocation.MyCommand.Path)
$loggerPath = $path + "\Logger\release\Logger.ps1";
.$loggerPath;
$logger = Logger;
$logger.load($path + "\Logger\config\log4ps.xml","log.log");
and what is the best way to do it with regard to my problem?
How do I create a temp folder in windows temp folder?
Common Code In Powershell
You can just put the code you want to include in a different PS1 file, and then "dot source" that file to include it in the current scope:
. D:\script_common\MyCode.ps1
That's all there is to that.
Using a Module
You might consider using a module instead, which can be included using the Import-Module cmdlet. You might have used this to work with things like Active Directory, where you could do something like this:
Import-Module ActiveDirectory
In that case, you only need the name of the module because it's in a special directory.
To write your own modules in Powershell, you name the module with a .psm1 extension. Typically, you don't do free floating code in one of these; you write functions which are then available to the code which imports the module.
To import a script module from anywhere, use the full path:
Import-Module D:\script_common\MyModule.psm1
Module Paths
When you create your own modules, you can keep them any old place and then refer to them by their full path (as above). There are also several locations that Powershell looks for modules:
$PSHome\Modules (%Windir%\System32\WindowsPowerShell\v1.0\Modules) -- Reserved for modules that ship with Windows. Do not put things here.
$Home\Documents\WindowsPowerShell\Modules (%UserProfile%\Documents\WindowsPowerShell\Modules)
%ProgramFiles%\WindowsPowerShell\Modules -- this isn't mentioned in the link, and seems to be used more for Desired State Configuration modules (probably because it applies to the entire system).
These are defaults, but Powershell uses its own environment variable called PSModulePath to determine where to look, and much like PATH you can add your own folder(s) to that variable.
That lets you keep your modules in your own location. Do see the link for more info on how to structure your folders and how to do naming.
So as far as keeping your modules and "3rd party" modules in the same place, that depends on the 3rd party stuff. It may install its own modules in its own place and modify the path, or it may just let you put them wherever you want.
Creating a Temp Folder
You can use the TEMP or TMP environment variables to get the path of the temp folder. To retrieve them in Powershell, use $env:TEMP or $env:TMP.
You'll have to come up with a unique name of a folder to create in there. One way to do that might be to use a GUID:
$dirName = [System.Guid]::NewGuid().ToString()
New-Item -Path "$($env:TEMP)\$dirName"
You should be able to dot source the script like that:
. "C:\script_common\script.ps1"
after that you can use all the functions like they were in the script you are running.
But... the better way to do it would be to create a module with your common functions (How to here: Scripting Guy´s Blog. (TLDR Version: place functions into psm1 file, put into modulepath, import using Import-Module, profit)
For creating a folder:
New-Item C:\Temp\yourfolder -type directory
Here is my attempt to create a template system in powershell : https://github.com/kayasax/PowershellTemplate
It allows to reuse functions you save in the repository by using tags in the template
eg :
<include logging/log>
The content of the file log.ps1 found in the logging directory of the function repository will be inserted when tranforming template to script

Powershell import-module doesn't find modules

I'm learning PowerShell and I'm trying to build my own module library.
I've written a simple module XMLHelpers.psm1 and put in my folder $home/WindowsPowerShell/Modules.
When I do:
import-module full_path_to_XMLHelpers.psm1
It works. But when I do:
import-module XMLHelpers
It doesn't work and I get the error:
Import-Module : The specified module 'xmlhelpers' was not loaded because no valid module file was found in any module directory.
I've checked that the environment variable PSModulePath contains this folder. As it is a network folder, I've also tried to move it to a local folder and to modify PSModulePath but without success
$env:PSModulePath=$env:PSModulePath+";"+'C:\local'
Any idea on what could cause this issue?
The module needs to be placed in a folder with the same name as the module. In your case:
$home/WindowsPowerShell/Modules/XMLHelpers/
The full path would be:
$home/WindowsPowerShell/Modules/XMLHelpers/XMLHelpers.psm1
You would then be able to do:
import-module XMLHelpers
1.This will search XMLHelpers/XMLHelpers.psm1 in current folder
Import-Module (Resolve-Path('XMLHelpers'))
2.This will search XMLHelpers.psm1 in current folder
Import-Module (Resolve-Path('XMLHelpers.psm1'))
I think that the Import-Module is trying to find the module in the default directory C:\Windows\System32\WindowsPowerShell\v1.0\Modules.
Try to put the full path, or copy it to C:\Windows\System32\WindowsPowerShell\v1.0\Modules
I experienced the same error and tried numerous things before I succeeded. The solution was to prepend the path of the script to the relative path of the module like this:
// Note that .Path will only be available during script-execution
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
Import-Module $ScriptPath\Modules\Builder.psm1
Btw you should take a look at http://msdn.microsoft.com/en-us/library/dd878284(v=vs.85).aspx which states:
Beginning in Windows PowerShell 3.0, modules are imported automatically when any cmdlet or function in the module is used in a command. This feature works on any module in a directory that this included in the value of the PSModulePath environment variable ($env:PSModulePath)
I had this problem, but only in Visual Studio Code, not in ISE. Turns out I was using an x86 session in VSCode. I displayed the PowerShell Session Menu and switched to the x64 session, and all the modules began working without full paths. I am using Version 1.17.2, architecture x64 of VSCode. My modules were stored in the C:\Windows\System32\WindowsPowerShell\v1.0\Modules directory.
Some plugins require one to run as an Administrator and will not load unless one has those credentials active in the shell.
My finding with PS 5.0 on Windows 7: $ENV:PsModulePath has to end with a . This normally means it will load all modules in that path.
I'm not able to add a single module to $env:PsModulePath and get it to load with Import-Module ExampleModule. I have to use the full path to the module. e.g. C:\MyModules\ExampleModule. I am sure it used to work.
For example:
Say I have the modules:
C:\MyModules\ExampleModule
C:\MyModules\FishingModule
I need to add C:\MyModules\ to $env:PsModulePath, which will allow me to do
Import-Module ExampleModule
Import-Module FishingModule
If for some reason, I didn't want FishingModule, I thought I could add C:\MyModules\ExampleModule only (no trailing \), but this doesn't seem to work now. To load it, I have to Import-Module C:\MyModules\ExampleModule
Interestingly, in both cases, doing Get-Module -ListAvailable, shows the modules, but it won't import. Although, the module's cmdlets seem to work anyway.
AFAIK, to get the automatic import to work, one has to add the name of the function to FunctionsToExport in the manifest (.psd1) file. Adding FunctionsToExport = '*', breaks the auto load. You can still have Export-ModuleMember -Function * in the module file (.psm1).
These are my findings. Whether there's been a change or my computer is broken, remains to be seen. HTH
try with below on powershell:
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
import-module [\path\]XMLHelpers.psm1
Instead of [] put the full path
Full explanation of this and that
First of all check Your account type,some imports are not allowed to normal partner accounts.