<commit_before>---
layout: default
title: Creating Reusable PowerShell Modules with PsGet and Chocolatey
---
# Creating Reusable PowerShell Modules with PsGet and Chocolatey

As automation becomes more popular, teams need a way to organize scripts in source control and ultimately deliver those scripts to a runtime environment in a consistent and repeatable way. 

## The Problems

### Install
I have a few PowerShell modules that I work with that I'd like to distribute internally but I don't want to manually copy the modules into the PowerShell modules folders. 

### Dependency Management
I would also like to benefit from package management and not need to copy folders and files around in order to get reuse out of my modules. If a module has a dependency, it should automatically download that dependency.

### Learning Curve
I would like to give the Operations team a one liner that they can use to install the latest package dependencies and not have to worry about digging around on google to find out how to install the module. 

### I want it now
Windows Management Framework v5.0 is currently in Preview and has package management capabilities that I can't use because they are not production ready. 

## The Solutions
### NuGet - Dependency Management

NuGet allows for an application to set a dependency on a released package. If the package has dependencies, those dependencies are downloaded automatically when the package is resolved. 
The ability to resolve packages solves the issue of managing PowerShell dependencies. 
NuGet out of the box is great, but it is really meant for managing development dependencies and requires some extra extension to manage software. 

This leads us to...

### Chocolatey - Software as Packages

Built upon NuGet, [chocolatey](https://chocolatey.org/) takes the concept of packages and applies it to software installs. 
This gives the consumer the ability to script the install of software and not just development artifacts.  

So the install of something like Java is reduced to something like:

```
cinst jre8
```

### PsGet - Automate Install of PowerShell Modules

But what about PowerShell modules? PowerShell modules aren't installed in the traditional sense, they are really copied to a folder in the user's PowerShell Modules directory. 
Enter [PsGet](http://psget.net/). PsGet allows you to install a module by pointing to a .psm1 file. 

### Putting it all together

We now have individual solutions for all of our problems above, so lets put it all together and build a Chocolatey package for our IO module. 

## Directory Structure

### Source Control

I store my powershell modules in a single Git repo using subfolders for each module. You could easily modify this and store a module per git repo. 

### On Disk

For this example I'm going to create a Chocolatey package called "IO" that has a Cmdlet called "Join-Uri". 
The Join-Uri cmdlet allows me to combine a Uri with a child path and works very similar to the Join-Path cmdlet. 

The folder you create here should match the module name that you want to create.

I will start by creating a folder called "IO" and placing a PowerShell script called Join-Uri.ps1 in the folder. 
Running DIR on the folder I see:

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1  
```

Here are the contents of the Join-Uri.ps1 script

```powershell
<# Joins uri to a child path#>
function Join-Uri
{
    [CmdletBinding(DefaultParametersetName="Uri")]    
    param(
        [Parameter(ParameterSetName="Uri", Mandatory=$true, Position=0)]
        [uri]$uri, 
        [Parameter(ParameterSetName="Uri", Mandatory=$true, Position=1)]
        [string]$childPath)
    $combinedPath = [system.io.path]::Combine($uri.AbsoluteUri, $childPath)
    $combinedPath = $combinedPath.Replace('\', '/')
    return New-Object uri $combinedPath
}
```

Pretty simple. It just takes a path and combines it using System.IO.Path.Combine and rewrites the backslashes to forward slashes. 

Any other cmdlets that I need to add will be added as individual .ps1 files. 
In order to support this structure, we need to create a PowerShell module file .psm1 that understands how to read individual script files. 
We will create a psm1 files called "IO.psm1" in the directory. 
The file name should match the name of the module you want to create and because of that it should be the same as the directory that contains it.
EX: folder "IO" holds "IO.psm1".

Here is the output of the DIR command. 

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                      
-a---         3/17/2015   3:52 PM        171 IO.psm1                                                                                                    
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1   
```

Here are the contents of the IO.psm1 file

```powershell
# Load all script files recursively into this module
# http://www.kmerwin.com/?p=174
gci $psscriptroot\*.ps1 -exclude ChocolateyInstall.ps1 -Recurse | % {. $_.FullName }
```

Special shout out to the blog at kmerwin.com for originating this idea for organizing files. 
I added an exclude filter in there to make sure I don't include the ChocolateyInstall.ps1 file in actual module. 
The ChocolateyInstall.ps1 file is the logic that installs our chocolatey package.
We will add that file now.

First, create a tools folder. In the tools folder add a ChocolateyInstall.ps1 file. Running DIR on the root folder will show:

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                       
d----         3/17/2015   4:02 PM            tools                                                                                                      
-a---         3/17/2015   3:52 PM        171 IO.psm1                                                                                                    
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1                                                                                               
<commit_msg>adds link to blog post for organizing powershell modules<commit_after>---
layout: default
title: Creating Reusable PowerShell Modules with PsGet and Chocolatey
---
# Creating Reusable PowerShell Modules with PsGet and Chocolatey

As automation becomes more popular, teams need a way to organize scripts in source control and ultimately deliver those scripts to a runtime environment in a consistent and repeatable way. 

## The Problems

### Install
I have a few PowerShell modules that I work with that I'd like to distribute internally but I don't want to manually copy the modules into the PowerShell modules folders. 

### Dependency Management
I would also like to benefit from package management and not need to copy folders and files around in order to get reuse out of my modules. If a module has a dependency, it should automatically download that dependency.

### Learning Curve
I would like to give the Operations team a one liner that they can use to install the latest package dependencies and not have to worry about digging around on google to find out how to install the module. 

### I want it now
Windows Management Framework v5.0 is currently in Preview and has package management capabilities that I can't use because they are not production ready. 

## The Solutions
### NuGet - Dependency Management

NuGet allows for an application to set a dependency on a released package. If the package has dependencies, those dependencies are downloaded automatically when the package is resolved. 
The ability to resolve packages solves the issue of managing PowerShell dependencies. 
NuGet out of the box is great, but it is really meant for managing development dependencies and requires some extra extension to manage software. 

This leads us to...

### Chocolatey - Software as Packages

Built upon NuGet, [chocolatey](https://chocolatey.org/) takes the concept of packages and applies it to software installs. 
This gives the consumer the ability to script the install of software and not just development artifacts.  

So the install of something like Java is reduced to something like:

```
cinst jre8
```

### PsGet - Automate Install of PowerShell Modules

But what about PowerShell modules? PowerShell modules aren't installed in the traditional sense, they are really copied to a folder in the user's PowerShell Modules directory. 
Enter [PsGet](http://psget.net/). PsGet allows you to install a module by pointing to a .psm1 file. 

### Putting it all together

We now have individual solutions for all of our problems above, so lets put it all together and build a Chocolatey package for our IO module. 

## Directory Structure

### Source Control

I store my powershell modules in a single Git repo using subfolders for each module. You could easily modify this and store a module per git repo. 

### On Disk

For this example I'm going to create a Chocolatey package called "IO" that has a Cmdlet called "Join-Uri". 
The Join-Uri cmdlet allows me to combine a Uri with a child path and works very similar to the Join-Path cmdlet. 

The folder you create here should match the module name that you want to create.

I will start by creating a folder called "IO" and placing a PowerShell script called Join-Uri.ps1 in the folder. 
Running DIR on the folder I see:

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1  
```

Here are the contents of the Join-Uri.ps1 script

```powershell
<# Joins uri to a child path#>
function Join-Uri
{
    [CmdletBinding(DefaultParametersetName="Uri")]    
    param(
        [Parameter(ParameterSetName="Uri", Mandatory=$true, Position=0)]
        [uri]$uri, 
        [Parameter(ParameterSetName="Uri", Mandatory=$true, Position=1)]
        [string]$childPath)
    $combinedPath = [system.io.path]::Combine($uri.AbsoluteUri, $childPath)
    $combinedPath = $combinedPath.Replace('\', '/')
    return New-Object uri $combinedPath
}
```

Pretty simple. It just takes a path and combines it using System.IO.Path.Combine and rewrites the backslashes to forward slashes. 

Any other cmdlets that I need to add will be added as individual .ps1 files. 
In order to support this structure, we need to create a PowerShell module file .psm1 that understands how to read individual script files. 
We will create a psm1 files called "IO.psm1" in the directory. 
The file name should match the name of the module you want to create and because of that it should be the same as the directory that contains it.
EX: folder "IO" holds "IO.psm1".

Here is the output of the DIR command. 

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                      
-a---         3/17/2015   3:52 PM        171 IO.psm1                                                                                                    
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1   
```

Here are the contents of the IO.psm1 file

```powershell
# Load all script files recursively into this module
# http://www.kmerwin.com/?p=174
gci $psscriptroot\*.ps1 -exclude ChocolateyInstall.ps1 -Recurse | % {. $_.FullName }
```

Special shout out to the blog at [kmerwin.com](http://www.kmerwin.com/?p=174) for originating this idea for organizing files. 
I added an exclude filter in there to make sure I don't include the ChocolateyInstall.ps1 file in actual module. 
The ChocolateyInstall.ps1 file is the logic that installs our chocolatey package.
We will add that file now.

First, create a tools folder. In the tools folder add a ChocolateyInstall.ps1 file. Running DIR on the root folder will show:

```
Directory: \IO

Mode                LastWriteTime     Length Name                                                                                                       
----                -------------     ------ ----                                                                                                       
d----         3/17/2015   4:02 PM            tools                                                                                                      
-a---         3/17/2015   3:52 PM        171 IO.psm1                                                                                                    
-a---         3/17/2015  11:29 AM        492 Join-Uri.ps1                                                                                               

{'fraction_non_alphanumeric': 0.06384839650145772, 'fraction_numerical': 0.015524781341107873, 'mean_word_length': 1.8209292763157894, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 84, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5835344', 'n_tokens_mistral': 3271, 'n_tokens_neox': 3007, 'n_words': 1608}