If your organization is serious about using Chocolatey as a means for Windows package management, then setting up your own internal package repository is a must.

While, the Chocolatey repository is full of great Windows packages, chances are you do not want all your internal client machines reaching out to the internet for every package install. It is obviously insecure, not mention will undoubtedly be slower.

There is a slew of solutions for hosting your own repository, such as ProGet, Teamcity, SCCM or just a plain old file share. In this article, I will illustrate setting up a Chocolatey Simple Server, which is an IIS web server with some NuGet framework stuff. It is a great choice if your organization is on the small-medium side due to the cost (free) and setup.

Installing Chocolatey Server with Chocolatey

There are a few ways to install Chocolatey Server, but the one I prefer is using the Puppet module. The reason for this is that it automates almost all that you will need to get your server up and running. What the module does not do is some added configuration that I will add on in Puppet. These configurations are setting the firewall, configuring the app pool identify and setting a virtual directory to point to a UNC share which will house our Chocolatey packages. Keep in mind there could be others depending on your needs such as using an SSL certificate.

If you do not want to use Puppet for this, you can simple use the Chocolatey CLI to install it, which will do most of the setup:

PS C:\Users\Administrator> choco install chocolatey.server –y

Creating Puppet configuration for Chocolatey Server

On my Puppet master server, I will first install the Chocolatey Server module:

[vagrant@puppet production]$ puppet module install chocolatey-chocolatey_server



Notice: Preparing to install into /home/vagrant/.puppetlabs/etc/code/modules ...



Notice: Downloading from https://forgeapi.puppet.com ...







Notice: Installing -- do not interrupt ...



/home/vagrant/.puppetlabs/etc/code/modules



└─┬ chocolatey-chocolatey_server (v0.0.5)



├─┬ puppet-iis (v1.4.1)



│ └── puppetlabs-stdlib (v4.24.0)



├── puppet-windowsfeature (v1.1.0)



├── puppetlabs-acl (v1.1.2)



└── puppetlabs-powershell (v1.0.6)

You can see it installs some other dependencies such as puppet-iis, windowsfeature and powershell. For this example, I will also install these modules from Puppet forge:

[vagrant@puppet production]$ puppet module install chocolatey-chocolatey



[vagrant@puppet production]$ puppet module install puppet-windows_firewall

Now, I will create my own Puppet module, using puppet module generate in order to create some additional configurations for my server.

[vagrant@puppet production]$ puppet module generate --skip-interview my-chocoserver

From here, I want to edit the init.pp file in my module and create my class that I will call in my main manifest. My class, will do the following:

Install the Chocolatey Server using the chocolatey_server module

Allow a firewall exception for port 8080 (which I will use for the Chocolatey Server)

Ensure Basic authentication is installed

Create a virtual directory pointing to a UNC share

Change the App pool to run under the network service

Increase the MaxAllowedContentLength on my web server to 50000000. This allows for larger packages.

Here is the init.pp:

class chocoserver {



class {'chocolatey_server':



port => '8080',



}







windows_firewall::exception { 'Choco server':



ensure => present,



direction => 'in',



action => 'Allow',



enabled => 'yes',



protocol => 'TCP',



local_port => '8080',



remote_port => 'any',



display_name => 'Chocolatey Simple Server',



description => 'Inbound rule for Chocolatey Server. [TCP 8080]',



}







windowsfeature { 'Web-Basic-Auth':



ensure => present,



require => Class["chocolatey_server"]



}







exec { 'VirtualDir':



command => '$(Import-Module WebAdministration;New-Item IIS:\Sites\chocolatey.server\App_Data\Packages -type VirtualDirectory -physicalPath \\\\server\\packages$ -Force)',



provider => powershell,



logoutput => true,



require => Class["chocolatey_server"],



}







exec { 'ChocoAppPoolID':



command => '$(Import-Module WebAdministration;Set-ItemProperty IIS:\AppPools\chocolatey.server -name processModel -value @{identitytype=2})',



provider => powershell,



logoutput => true,



require => Class["chocolatey_server"],



}







exec { 'MaxAllowedContentLength':



command => '$(Import-Module WebAdministration;Set-WebConfigurationProperty -filter /system.webserver/security/requestfiltering/requestLimits -name maxAllowedContentLength -value 50000000)',



provider => powershell,



logoutput => true,



require => Class["chocolatey_server"],



}







}

As you can see the first thing we do in our “chocoserver” class is ensuring that “chocolatey_server” is installed and running on port 8080. From here, we ensure the firewall has 8080 open.

To show how PowerShell can be used in Puppet, I use the exec resource to run PowerShell code. In the first example in the class, I run:

Import-Module WebAdministration;New-Item IIS:\Sites\chocolatey.server\App_Data\Packages -type VirtualDirectory -physicalPath \\\\server\\packages$ -Force

What this does is create a virtual directory in our Chocolatey server and point it to a UNC share - \\server\packages$. This ensures that our data is kept separate from our server. To ensure that our app pool identify has permission, we change the app pool identity for chocolatey.server to “network service”. Keep in mind that means the computer object needs permission on the UNC share. Note - As you can see to indicate a UNC path in Puppet you need to add two extra slashes for the server and one for the share name.

Related: Chocolatey Is Quite Yummy

Finally, the “maxAllowedContentLength” increases the site to allow for large packages, in this case 50GB (excessive I know!).

Now, in our main manifest file (site.pp in this case), we include this:

node 'puppetagent-win' {



include chocolatey



include chocoserver



}

This guarantees that the Chocolatey client is installed and then our chocoserver module, that includes Chocolatey Server.

Now on our puppet node we just run this to apply the configuration and install our Chocolatey Server:

PS C:\Users\Administrator> puppet agent –test

As you can see below, running Get-WebSite on our node in PowerShell shows the web server is up and running:

PS C:\Users\Administrator> Get-Website







Name ID State Physical Path Bindings



---- -- ----- ------------- --------



chocolatey.server 1 Started C:\tools\chocolatey.server http *:8080:

As I noted before there may be additional changes you want to make to your internal repo. One would be to change the API key, needed to push packages. To do this, change the web.config in c:\tools\chocolatey.server:

<add key="apiKey" value="chocolateyrocks" />

Adding and installing Chocolatey Packages

To add new packages to our internal repository, we use the command choco push. In this example, I want to push my package named “test”. I will need to point to the directory my package is located, in this case it is c:\testpackage.

PS C:\Users\Administrator> choco push c:\testpackage --source "'http://choco-server:8080'" -k="'MyOwnApiKey"

To install the test package from my internal repository I just run: