Use the Configuration-as-Code Plugin to Declaratively Configure Your Jenkins Instance

Introduction

Note: If you already know well what configuration management is, you can skip the introduction.

For many years now, there has been a steady trend of configuring everything we can using code. This makes it much easier to avoid snowflake systems, hence also making orders of magnitudes easier to reprovision an environment from scratch if needed.

Developers have been configuring their Jenkins instances following this principle for a long time, generally using a neat combination of init.groovy.d scripts, file manipulations, Docker images and so on, as well as many other technologies.

The issue is that some, if not most, of those settings require a deep dive into how Jenkins was designed in order to write, for example, Groovy scripts that would literally change Java objects from the internal Jenkins model.

This is definitely a very powerful Jenkins feature that rewards persistent developers to achieve whatever they need. However, it is also a very sharp tool — effective for experts, but can be dangerous and discouraging for newcomers.

Going declarative

These days, the trend is to provide tooling and APIs that allow for so-called idempotency and declarativeness. This has been mostly popularized by configuration management tools like CfEngine, Puppet, Chef, and more recently with tools like Ansible. (To some extent, some will say Docker and container technologies have certainly lowered the critical need for some of the features offered by these tools. But let’s table that part of the discussion for later and avoid starting a flame war!)

In concrete terms, it means I, as a developer, should not have to understand complicated features of a given tool to configure it. I should be allowed to just say something readable and expressive like:

  • the public URL of this service is this
  • a file named blah should be there, and contain that
  • etc.

A similar feature is being developed for Jenkins.

Jenkins configuration-as-code plugin, aka JCasC

Implementing JCasC to support configuring out of the box virtually any Jenkins feature or plugin works fine most of the time. But there are times when it doesn’t. For instance when a plugin didn’t follow the usual Jenkins plugin development patterns, the development team has to react to address issues and/or create specific adapters to support some specific cases. (Obviously, development resources and interest are not infinite, so JCasC works best on plugins whose statistics that show a decently large number of installs. If not, then the team is always happy to accept pull-requests).

Format and simple example

You define the configuration using a yaml formatted file.

The format is generally structured as follows:

some_configuration_category:
  <KEY_1>: <VALUE_1>
  <KEY_2>: <VALUE_2>
  ...

For instance, to configure the Jenkins system message that is always shown on the top of the UI and set the number of executors to 0 on the master, you simply do the following:

jenkins:
  systemMessage: "Welcome to Jenkins - configured with code!"
  numExecutors: 0

Structuring your configuration

You can either have a single big yaml file for configuring your instance, or separate the various aspects of the configuration in many smaller files for clarity and maintenance purposes, and point the Configuration As Code plugin to the directory of those files. The plugin uses the CASC_JENKINS_CONFIG environment variable to define this value.

I personally recommend the latter, as I feel this keeps the configuration chunk sizes smaller, and lets you name the associated file in a way that makes its role as clear as possible.

We use this principle in Jenkins Evergreen for instance.

Passing environment variables or secrets

There are obviously some values that you do not want to store in the file, be it because it is a secret, or because it is an external value that keeps changing and has to be provided more dynamically by the execution environment.

To handle this case, the plugin offers a nice variety of solutions: transparently handling the variables source from the environment, Docker secrets or HashiCorp Vault, at the time of this writing. 

Example configuring transparent native storage of artifacts and Pipeline stashes in Amazon S3:

unclassified:
  artifactManager:
    artifactManagerFactories:
      - jclouds:
          provider: s3
aws:
  awsCredentials:
    region: "us-east-1"
  s3:
    container: "${ARTIFACT_MANAGER_S3_BUCKET_NAME}"
    prefix: "jenkins_data/"

What is that unclassified category?

To provide more clarity and structure over configuration, the plugin has reused the existing infrastructure to allow defining symbols inside plugins. In this case, it means that generally plugins have to simply annotate some of their main code so it will then be configurable in a more specific category, offering more expressiveness and some form of documentation as code to end users.

Awesome, how do I get started?

First, you probably want to have a look at the various demo examples in the plugin source repository itself, to get a feeling of what you are going to do. Then, you should start small by using Docker, for instance, to be able to play with the plugin locally and iterate quickly. This will ensure you are able to configure small things first, before growing your configuration.

If you are starting from scratch, then probably the above configuration and iterating should be enough. You’ll also definitely want to reach out to the development team on their Gitter channel.

Exporting an existing setup?

If you wish to retrofit an existing setup into the new configuration, then you will obviously want to take more time. If you are already using init.groovy.d scripts, you can progressively replace parts of these scripts with its equivalent by using JCasC.

Though this is not currently totally possible to export an existing setup, the development team is working on providing more and more tooling to be able to make this task easier. 

Under https://YOUR_JENKINS/manage, you will find a dedicated configuration-as-code entry, where you can trigger many actions.
Specifically Export Configuration exports the current setup as yaml.
This has been added very recently, so you will most probably need to adjust what is generated, but it definitely looks very promising.

For more up-to-date information, please refer to the configuration section in the official documentation.

Conclusion

I hope this article gave you a good overview so that you’ll want to dig into this new great tool!

Again, do not hesitate to reach out to the development team itself in the Jenkins Gitter chat channel, or even use the large Jenkins developers mailing list.