Hello tech world, this is Techthoughts and
welcome to this special Techthoughts video series focused on learning PowerShell.
This is an operationally designed video series that aims to get you ramped up
and using PowerShell quickly. This is video 13 in the series, PowerShell Modules.
As always if you prefer written documentation the corresponding article for this topic
can be found on the techthoughts.info blog which I've linked In the description
below. Let's go ahead and get started. I'm going to be demoing a lot of code in this
episode. Don't forget that all of the code examples are available on the Learn PowerShell
GitHub repository for easy reference. You'll find a link in the description below. In previous
episodes we covered PowerShell scripts and PowerShell functions where we began to create
Solutions and solve problems using PowerShell . It's common to stay at the stage for quite a while
as you experiment and get better with PowerShell. But as you continue in your PowerShell journey you
will eventually find that you want to enhance the capabilities of PowerShell through the use
of PowerShell modules. Additionally you may want to share something you create with others. To
effectively do either you'll need an understanding of how to work with PowerShell modules.
A PowerShell module is just structured PowerShell code that can be easily reused. Let's get a better
understanding of these structured components that make up a PowerShell module. Here I have a script,
and note the PS1 extension. And inside the script I have an array declared and it gets loaded with
some custom PowerShell objects that contain information about the various episodes in the
Learn PowerShell Series. So here we see that we're loading episode 1 and it contains information
about the blog link, YouTube link, and GitHub link. And we have a custom PowerShell
object for each episode in the series. I'm going to come back up here and collapse this
region which contains all of these custom objects, and notice down below in the main section
that I do have a function here called Get-LearnPowerShellInfo which is going to enable
the user to go ahead and run this cmdlet and get some information about various episodes in
the Learn PowerShell Series. So I'm going to go ahead and load this entire script into memory
with control a, and f8 on my keyboard. And that's going to load the entire contents of that script
into memory. And because we have that now I can go ahead and run this particular function
Get-LearnPowerShellInfo and I can specify for example an episode number of 12. And notice down here that
we get some information back from that custom object that contains information about episode
12 which is managing cloud with PowerShell. We're not covering PowerShell functions or logic
in this episode as I've covered those topics previously. You can go back and check out those
episodes if you need a quick refresher. Okay so why isn't this PS1 script good enough? It functions
clearly and enables the capabilities that we want. Well, it's just not very efficient from a code
sharing perspective. The script has to be shared with someone. They have to save it to their file
system and then load the script into memory like we just did. And there's no clean way to source new
versions of the script if it's updated or if bug fixes are made. It's also difficult for your code
to take a formal dependency on a script like this. What I want to show now is that we can convert the
script into a PowerShell module file by changing the extension to psm1. And note that I've already
done that and you see the psm1 file over here. This psm1 file is exactly the same as the script
that I just showcased. It has literally just had its extension changed to dot psm1. This module
file is the minimum requirement for a working PowerShell module. Because this is now a psm1 I can
perform actions with it that were not previously available to me. So I'm going to go ahead and
terminate this console which will remove that script that we previously loaded out of memory by
starting a new console. So I went ahead and deleted that it's going to ask me if I want to restart a
session I'm going to say yes. And now we'll launch a fresh integrated console. I'm going to change
to the directory that contains the psm1 file. And what I'm going to do now is I'm going to
run import module against the learn PowerShell psm1. And I'm going to go ahead and click enter. So because we didn't get any errors that means
that the module successfully imported. And again all we did was run Import-Module against
the psm1 file and again that's just a script that we changed the extension to psm1, which is now
enabling us to import using the Import-Module. So what is this doing for us? Well now we can run
Get-Module against learnpowershell, which is the name of the module that we just import. And that's
picking that up from the name of the module here. And notice that if we execute that that we do see
that a module has been loaded inside of memory and notice that we have the exported function
that we had specified in this particular psm1 file, the Get-LearnPowerShellInfo function.
If we had 20 or 30 functions in this psm1 a user of this module could now explore each and run
help and engage our code like a reusable toolset. And because we have this in a module construct
we can see which commands are associated with this particular module. But, notice that while this works
it lacks certain capabilities. For instance, note in the example the module has a version of 0.0 .
This is because the module file, the psm1 does not have the ability to provide details such as the
module version. This is why most module authors include a PowerShell module manifest. In fact, it's
a requirement if you want to publish a module to a repository. It's extremely easy to create a new
module manifest because PowerShell contains native cmdlets to do that and I'll go ahead and demonstrate
that now. I'm going to click Ctrl n on my keyboard and open up a new tab of PowerShell here and Ctrl
tilde on my keyboard to stash this window down. And I'll go ahead and run the command
New-ModuleManifest and all we have to do is provide a path parameter I'll take it right here in
the default location that we're already in and we'll call this learnpowershell.psd1 .
And a psd1 extension indicates a module manifest file. The name of the manifest file needs to
be the same as the name of your module. And we'll go ahead and click f8 on the keyboard and notice over here on the left that
we now have a new learnpowershell.psd1 . A module manifest is just a file that
contains a hash table of keys and values. These keys and values make up the metadata
of your PowerShell module and this metadata provides a lot of detail about your module. The
New-ModuleManifest command went ahead and generated a randomized guid for our particular
module so this can be tracked in a repository such as the PowerShell Gallery. It populated the
author of this module taking the default user that we ran this command as. And you can come through
each of these different values and populate it with information that's applicable to your module.
All this metadata isn't just informational though. Changes here can have a significant impact
on how your module operates. For instance, you can control which version of PowerShell your
module will run on. And you can also specify required modules that will be installed if
your module has dependencies, and much more! You can consult the documentation on how to write
a PowerShell module manifest as it details what each of these keys is for and when to use them.
You'll find a link on the techthoughts.info blog. I'm going to go ahead and close this
example one that we generated here. And I'm going to go ahead and delete that. I've
previously run that new module manifest command and I've already populated the psd1 with some
information about the learnpowershell module that we're creating here as an example. And so notice
here that I've put my author name, company name is Tech thoughts, and I've written a description about
what the module is going to do so on and so forth. Through the magic of editing I wanted to cover
one particular cmdlet that I neglected to mention in the first recording of this and that is
the Test-ModuleManifest command. And so as you're editing your manifest file to make sure
that you don't enter any type of syntax errors or enter some kind of construct that's not
permitted in the manifest, it's a good idea to occasionally run the Test-ModuleManifest
command against your psd1 file to ensure it's still valid. And so you can execute this using f8
on the keyboard and so if it returns this instead of an error that means that your manifest file
is valid and can be published to a repository. So if I list the contents of this directory
notice that we do have our psm1 file which contains the raw logic and information
and functionality of our particular module. And we also have the psd1 file that's
populated with the metadata of our module. If we import the psd1 file... note, not the
psm1. We're importing the manifest file. That if we run a Get-Module against learnpowershell
that we now have a versioned PowerShell module as shown here. And this reflects the
manifest information that we populated in the psd1. Now if we make updates to our module the module
manifest version can be incremented and we can release new versions of this module. Now that we've
covered the basics of PowerShell module components let's dive into finding, evaluating, and installing
PowerShell modules. PowerShell modules are sourced from a repository, the most popular being the
PowerShell Gallery public repository. And at the time of this recording you can see there are over
10,000 unique module packages that are available for download. We can show what repositories that
PowerShell is configured to use by running the Get-PSRepository cmdlet. So here we have
Get-PSRepository, I'll run that with f8 on my keyboard and notice that by default PowerShell is
configured to use the PowerShell Gallery indicated here with PSGallery. If we
run this and pipe that to Format-List and star, again running with f8, we can see that
the PowerShell Gallery is just a nuget package management provider and because we are defaulted
to utilizing this we'll be able to use commands like Find-Module, Install-Module, and Save-Module
which will be able to interact with the PowerShell Gallery by default. So we'll start off with a
Find-Module, and we can provide that a tag is a great way to search for PowerShell modules on the
Gallery. And if you wanted your code to be able to do something, for example, with Telegram which is a
chat client, you could come in here and provide it the Telegram tag and see if the PowerShell Gallery
has any modules that have Telegram capability. Here we see that returned one result which is PoshGram.
You can of course pipe this to Format-List as well and retrieve some additional information about
that particular module. So here we see we get a lot more information back including the project
URI so we could go visit and check it out on GitHub. We could see who authored the module, what
version it's on, a description about the module, so on and so forth. If I wanted to install this
module I would simply run Install-Module, provide it the name, we saw that as PoshGram.
And we could scope this. And we could scope it either to all users, so that all users would
have access to this module. Or we could scope the install to just this current user and
so only this current user that I'm logged in as would have access to this particular
module. I'll go ahead and scope it to the current user and I'm going to go run this
Install-Module using f8 on my keyboard. Notice that I'm getting a prompt about an
untrusted repository and it's asking me to confirm if I would like to install this module from the
PowerShell Gallery. I'll go ahead and say yes and the Install-Module handles everything
for you. It went out and reached to the PowerShell Gallery downloaded and installed
that module onto this particular system. The reason that we got that prompt is
notice if we run Get-PSRepository again that currently the PowerShell Gallery
is by default an untrusted repository. You can adjust this in the configuration
settings of your PowerShell repository and elect to set the PowerShell Gallery
as a trusted Source if you desire. It is possible to register additional repositories
using the Register-PSRepository cmdlet. Check out the techthoughts.info blog for
additional information as this can be used to register things like private repositories
or internal repositories for your particular Enterprise. PowerShell modules can also be
installed manually. The psd1 and psm1 files simply need to be placed in a folder in the environment
PS module path. This is a reserved variable that contains the paths that PowerShell will look in
to natively discover PowerShell modules. Notice if I click f8 on my keyboard that this lists out a couple
of directories and these are the directories that PowerShell is going to check to see if it can
find particular PowerShell modules. Notice that if I start typing in the Send-TelegramTextMessage
command that that went ahead and auto completed and I'm getting intellisense for the
bot token, the chat ID, and the message that I want to send. The reason that PowerShell is able to auto
source and autocomplete this for this intellisense for this cmdlet is because it is checking to
see if the Send-TelegramTextMessage command is available in one of these locations. And again
it's getting that from the environment PS module path variable. I'm going to go ahead and kill this
terminal again and start with a fresh session. And note that in this session, because we have not
manually imported the module that we've created as an example, that we do not get the
Get-LearnPowerShellInfo auto completion and PowerShell is not aware that that function exists on the system.
And so what we can do is again we'll evaluate the environment PS module path command and
what we need to do is place the psd1 and the psm1 files that we previously created into
one of these locations to manually install this. We could place the psd1 and psm1 in the
C:\Program Files\PowerShell\Modules location. We could also place it in the documents slash
PowerShell slash modules location. And so that's what I'll elect to do.
So what we'll do is we'll come in here, into documents PowerShell modules
and I'll create a new folder and it needs to be the name of the module that we
created. So this is going to be learnpowershell. And now that I have that what I can do is I can copy in the files that we previously created,
the psd1 and the psm1 into that location. Now that the manifest file and the module file are inside of this location, which again is being
sourced by the environment PS module path, if I type in Get-LearnPowerShellInfo I'm
now getting auto completion, because when I clicked tab on my keyboard PowerShell went
ahead and scanned through everything in the environment PS module path to see if it could find
a module that contained that particular function. So this is how you can manually install a module
on your system. Note that because we installed this module in this particular fashion that we won't
be able to do things like use Update-Module to update this from the Gallery moving forward. And so
once you have taken a manual approach to a module install on your system you will need to continue
to interact with it in a manual fashion. If you wanted to uninstall this in a manual fashion it
would be as simple as coming into the modules location and deleting this particular folder.
And that module is now deleted from the system. Sometimes you might want to evaluate a PowerShell
module without installing it. And this can be accomplished using Save-Module. This is
going to ask us for some very similar information and we'll provide a module name like PoshGram.
And because we're doing a save we also need to provide it a path. And so we'll put it in C:\eval and
if we run this command what's going to happen is it's going to go out to the PowerShell Gallery,
because that's our default repository, pull down the PoshGram file, and save it to the C:\eval
location. So I'll go ahead and run this using f8. And if we visit that location on the file system notice that it has downloaded this particular
module. And now what we can start doing is we can start evaluating the contents of this
particular module. So I could for example drag this psm1 file into VSCode and I can start
evaluating and inspecting this particular logic to make sure that it does what I think it
does prior to getting installed on my machine. Because this is in a location that is not part of
the environment PS module path your system will not be able to auto source from this location.
But of course you can navigate to this location. So we'll change directory to that save path so
C:\eval and because if I list out the contents of this it contains a psd1 we could obviously
import the module manually here from this location. And that would allow us to access and evaluate
and test different functions inside of this module without actually installing it on our system.
So Save-Module is a great function to know. Okay so wrapping up with a final example let's
publish this learnpowershell module so that people around the world can utilize it and
learn more about the Learn PowerShell Series using PowerShell! In order to publish
a module to the PowerShell Gallery you will need to sign in with an account. It's
generally tied to your Microsoft account. And once you're logged in you'll have access to
an API key which will allow you to publish your modules to the PowerShell Gallery and
share them with users around the world. There is documentation for how to
create a PowerShell Gallery account, recommended best practices for utilizing your
PowerShell Gallery account, and you can again find this link on the techthoughts.info blog. In order
to get this module published what we're going to utilize is the Publish-Module cmdlet. And
in order for the Publish-Module cmdlet to be able to run this particular cmdlet needs
to be able to source the module on our system. And as we previously discussed in order for
PowerShell to be able to source a module it has to be inside of the environment PS module
path. So I need to make sure that my module is located inside of a location that is sourced by
the environment PS module path. And so I'm going to come in here again and create a folder that
matches the name of my module, learnpowershell, and because my module manifest file has a module
version of 0.7.0 I'm going to include that version here in a subfolder 0.7.0. And so notice up here
that we are going to a path that is included in the environment PS module path, our folder is
named the same as our module name, and we have the version number being specified here. I'm going
to navigate into that and I'm going to include the psd1 and psm1 files that we drafted previously. Now that these are in this location the Publish-Module
command is going to be able to source our module version from this particular
location. And we're going to specify a name, it needs to match what is inside of our manifest.
And so this is going to be learnpowershell. And now we need to specify our nuget API key. Again you're going to be retrieving that
from your PowerShell Gallery account. We're going to specify the repository that we're
going to be publishing this to, which of course is going to be the PSGallery, and we're going
to make sure that we specify the exact version that we want to publish out which is going
to be the required version of 0.7.0 and so let's quickly review this. We're going to be
using Publish-Module which is going to be able to source the learnpowershell module
because we placed it inside of a location that is source-able by our environment PS module path.
And because it has access to this it's going to be able to utilize our nuget API key which we got
from our PowerShell Gallery account, and publish this particular module and module manifest to
the PowerShell Gallery. And we're going to be publishing the required version of 0.7.0. I'm going
to go ahead and run this with f8 on my keyboard and this is utilizing the permissions of my API
key to publish that psd1 file and psm1 file to the PowerShell Gallery. And success! And because
this has now been published to the Gallery you... watching this video can run the Find-Module
command and specify the name learnpowershell we see that the 0.7.0 version has been
published and is available in the Gallery. And it has the description and information that
was previously entered into our psd1 manifest file. And so now you can practice at home using Save-Module,
Install-Module and get access to this particular basic module and explore
its capabilities on your own device. Now that you have a grasp on the fundamentals
of a PowerShell module let's go check out some source code of a popular published module
on the Gallery today. And so this is the GitHub link for the PoshGram PowerShell module
which adds Telegram capabilities to PowerShell. Now looking at this you're probably thinking,
hey wait a second there's a lot more going on here than a simple psd1 manifest and a psm1
module file! And you're right! Many module authors integrate continuous integration
and continuous deployment practices into their module development efforts. This enables
capabilities such as automated tests, formatting checks, help generation, and combining functions
together to build the project for publication. But come up here and take a look into
the source structure and at its heart every module still just has the basic
components that we just covered, a psd1 and a psm1. We didn't need any of this extra
tooling to publish our basic module to the Gallery a moment ago. And users from
around the world can still engage it. That said there are a lot of benefits to
adding CI CD capabilities to your module creation process. And we'll be covering Advanced
PowerShell Module building in a later episode. Make sure to subscribe to be notified when that
content becomes available. Don't forget that all of this information is available in written format
on the techthoughts.info blog. And if you found this video helpful go ahead and click that like
button and subscribe for more Tech thoughts videos!