Getting Started With Chocolatey 4 Business & Jenkins CI

Getting Started With Chocolatey 4 Business & Jenkins CI

I’ve had an disagreement recently with a colleague about the usage of open-source automation tools, especially Chocolatey in Business environments. A key point of this argument was the integration of new open source tools into long-existing, mostly commercial software based workflows.

One of the main reasons to use Chocolatey in an organization is its ability to integrate seamlessly with already existing automation infrastructure.

I’ve been able to integrate Chocolatey successfully with Jenkins CI, Puppet, legacy internal windows services as well batch files. The day I replaced the multi-100 line content of a legacy batch installer with a single line of:

choco install {package} -y

was a really good day!

In this blog post I’m going to give you, in two parts, an idea of how you could get started with Chocolatey at your organization:

  • a possible way to roll-out Chocolatey to your colleagues, and;
  • a collection of things to get you started integrating Chocolatey with a CI-server (in this case Jenkins, but probably applicable to anything else that does support PowerShell).

Deploying Chocolatey At Your Organization

You’ll want to make it as easy as possible, hence putting a batch file on a network share that’s accessible from everywhere in your organization is probably a good idea. If you’re just getting started with Choco I’d also suggest putting your first packages on such a share.

Let’s assume following share-layout:

\\your-server\choco\install
\\your-server\choco\packages

Go to Chocolatey Completely Offline Install and follow the instructions on how to set it up.

Your First Packages

Running choco list now looks pretty boring because there are no packages at your feed yet (the Chocolatey Community Repository was removed for your organization in the steps you followed for the Chocolatey Completely Offline Install). Let’s go ahead and download some package files from the Chocolatey Community Repository and put them into the network share we discussed above.

Now you should be able to:

  • List the available packages with choco list;
  • Search for available packages with choco search;
  • Install available packages with choco install;

You can also create your own packages, just type choco new <PACKAGE NAME> and see what happens.

So far, you’ve only been using open source features of Chocolatey, and you can use all this without limitation within an organization, but there are some things you may need to consider: the packages you’ve pulled in from the Chocolatey Community Repository probably rely on downloading external installers such as .exe, .MSI or external archives such as .zip files. You don’t want to do that in an organization as you’ll want to have complete reliability in your solution, and downloading from the internet could have reliability issues (not to mention trust). There’s two options:

  • Manually download the required resources, changing the source code of the packages install.ps1 and repackage every single one of the required packages;
  • Or you could simply use Package Internalizer;

Package Internalizer

If you’re using Chocolatey in an organization, you’ll really dig this. Just type choco download <PACKAGE NAME> --internalize and let the magic happen. This will automatically download all needed assets and put them into the generated package, so you’ll achieve the maximum availability.

Just keep in mind that you’ll have to license all hosts that are consumers of packages you manage via internalization. (Chocolatey for Business currently starts at just $600 USD for up to 35 machines, and is $16/machine/year with volume discounts thereafter - check out the Chocolatey Pricing page for up to date information).

Chocolatey + Jenkins

Let’s automate the automation!

To be able to use the following jobs you’ll need to know the basics of Jenkins job configurations. You’ll need to setup parameterized jobs with the PowerShell plugin and Chocolatey should already be installed on the corresponding Jenkins nodes.

This job just updates all the Chocolatey packages on the server.

choco upgrade all -y

Yes, it’s as easy as that!

This simple Jenkins-job allows you to internalize any package from the Chocolatey Community Repository with the click of a button:

# PowerShell script to internalize chocolatey packages from the 
#community feed to an internal server using Chocolatey 4 Business editions 
#'internalize' feature. This script is designed to be run from jenkins, P_* 
#variables are defined in jenkins job!

# section CREDS
$pkgs = $env:P_PKGLIST
$uncshare = $env:P_UNC_SHARE
$targetfolder = $env:P_DST_FOLDER
$targetserver = $env:P_DST_SRV
$apikey = $env:P_API_KEY

$envtmp = $env:temp
$tmpdir = "$envtmp\chocointernalize"
$basefeed = "https://chocolatey.org/api/v2/"
# endsection

function InternalizePkg($pkg) {
	Push-Location $tmpdir
	choco download --internalize $pkg --resources-location="$uncshare\$pkg" --source="$basefeed" --no-progress
	$genpkg = ((Get-ChildItem *.nupkg -recurse).FullName | Select-String -Pattern $pkg)
    if ($targetfolder) {
        Write-Output "&amp;amp;amp;amp;gt; copying package '$genpkg' to '$targetfolder'"
        if (-Not (Test-Path $targetfolder)) {
            New-Item $targetfolder -ItemType Directory -Force -Verbose
        }
        Copy-Item $genpkg $targetfolder -Verbose -ErrorAction "Stop"
    } else {
        Write-Output "&amp;amp;amp;amp;gt; pushing package '$genpkg' to '$targetserver'"
        choco push $genpkg --source="$targetserver" --api-key="$apikey" -Verbose
    }
    Write-Output "------------------------------------------------------------------------"
    Write-Output ""
	Pop-Location
}

if ((Test-Path $tmpdir)) {
	Remove-Item $tmpdir -Recurse -Force -Verbose
}
New-Item $tmpdir -ItemType Directory -Force -Verbose

$pkgs | ForEach-Object {
	InternalizePkg $_
}

Remove-Item $tmpdir -Recurse -Force -Verbose

This job reports what packages are outdated (i.e. a newer version of that package is available at the community feed):

$mrecipient = $env:P_MAIL_REC
$msender = $env:P_MAIL_SEND
$msmtp = $env:P_SMTP_SERVER
$availPkgs = @()

$chocoout = $(choco outdated)
$chocoout | ForEach-Object {
    $up = $_.Split("|")
    if ($up -And ($up[3] -ne "false")) {
			if ($up[1] -eq $up[2]) {
				$availPkgs += "$($up[0]): $($up[1]) -&amp;amp;gt; $($up[2])"
			}
		} 

}

$res = $availPkgs | Format-Table | Out-String
if ($res) {
	Send-MailMessage -To $mrecipient -Subject "Chocolatey Packages Outdated!" -Body $res -Verbose -ErrorAction "Stop" -From $msender -SmtpServer $msmtp
}

Remember that this setup, and these scripts are really minimalistic. When you’re working in an environment with a couple of users you may want additional feeds to create some logical package separation or access control. There are many use-cases on how to interact with a command-line utility such as Chocolatey, but I just wanted to give you a quick dive-in how my first contact with Chocolatey looked like.