2020/05/08 • 5 min read

Configuration management with Puppet

In the last years, we've been seeing a major shift in infrastructure management. Whereas before we all used large and complex run books and had to execute a myriad of manual actions (sometimes with the assistance of several scripts), nowadays we're using more and more tools based in code.

 

There are several advantages to this, most of them already covered in this great post by Maxime Drouet, but just to outline a few:

  • easier to maintain as the code is the documentation;
  • easier to reproduce and repeat;
  • allows version control and quality assurance
  • avoid configuration drift

 

 

As a quick and dirty example of the advantages of a Configuration Management (CM) tool, to deploy a specific application server in the past, we had a 30-page manual illustrating actions, commands and configuration parameters that had to be done to deploy the server. Imagine doing this on a pool of servers… so 30 pages, times X number of servers… would take days to deploy just a couple of them.  

 

With A CM tool, you take the time to write the code once and then, with minor changes (if needed) you can roll-out multiple servers with a couple of clicks since they all use the same code (which also executes much faster than a human can execute). In our case, a pool of servers went from about two weeks to a couple of days; to have everything deployed and available for use in production. 

 

The ecosystem for configuration management tools is rather diverse, with multiple players, each with their approach and set of advantages/disadvantages. 

 

Figure 1: CM software alternatives

 

For La Redoute case, we decided to adopt Open Source Puppet, mostly based on the ease we felt to adopt it, over the alternatives. But practically speaking, most of them will allow you to achieve very similar results. 

 

Using Puppet

Puppet uses a master and agent approach, where you have a master server that is used to host and manage the client machines, and a local agent installed on each client machine. The agent will be the worker that communicates with the master and executes the code that is assigned to the machine. 

 

With the Open Source version of Puppet, the master/client management can become a bit daunting, since there’s no built-in GUI available. Luckily, some Open Source tools can integrate and give us that GUI for easier management and orchestration. Some of those tools are Puppet SummaryPuppetboard and our adopted tool, Foreman. 

 

In this post, we’re not going into details on how to install these tools, but rather focus on an operational overview and the benefits they bring. 

 

So, after you have Puppet and some tool to help manage the integration and client machines, we need some code to execute on our servers. For Puppet, the main source of this code is the Puppet Forge, where you can find community and Puppet’s modules (note that although there are a fairly generous number of modules available, your specific need may not be already addressed, and you need to develop your module). 

 

Luckily, Puppet language is rather straight forward and some open-source tools allow us to jump in and start editing or creating our modules. I suggest Visual Studio Code and the Puppet plugin, coupled with Puppet’s own PDK. 

 

After you have these installed, just fire up VS Code and run the command (with CTRL+SHIFT+P) and type Puppet to get the list of available actions: 

 

Figure 2: Openning the puppet pdk in vs code

 

The New Module option will create the full structure for a Puppet module (just follow the wizard), and to finish, you can use the New Class to create the initial manifest file. Now we’re all set to start coding! 

 

Figure 3: Description of several file types in the puppet module

 

Here’s a rough example of a custom manifest that installs Notepad++:

 

# notepadplus  
#  
# A description of what this class does  
#  
# @summary A short summary of the purpose of this class  
#  
# @example  
#   include notepadplus  
class notepadplus ($ensure = $notepadplus::ensure) {  
  
  $notepadplus = 'npp.7.5.8.Installer.x64.exe'  
  
  case $ensure {  
    'absent': {  
      package { 'Notepad++ (64-bit x64)':  
        ensure          => absent,  
        source          => 'C:\\Program Files\\Notepad++\\uninstall.exe',  
        install_options => ['/S'],  
      }  
    }  
    default: {  
        # If server is hosted in Google Cloud Platform  
        if $facts['productname'] == 'Google Compute Engine' {  
          exec { 'Get NotepadPlus GCP':  
          command  => "gsutil cp gs://your_bucket/${notepadplus} c:/windows/temp/${notepadplus}",  
          provider => powershell,  
          notify   => Package['Notepad++ (64-bit x64)']  
          }  
        }  
        # If server is hosted in Azure  
        elsif $facts['virtual'] == 'hyperv' {  
          exec { 'Mount Azure share NotepadPlus':  
          command  => "\$validate = (Test-Path -Path Z:); if (\$validate -ne \$true) {net use Z: \\\\your_azure_share}",  
          provider => powershell,  
          before   => File['Copy Notepad++ binary']  
          }  
  
          file { 'Copy NotepadPlus binary':  
            ensure             => present,  
            source_permissions => ignore,  
            source             => "z:/${notepadplus}",  
            path               => "c:/windows/temp/${notepadplus}",  
            require            => Exec['Mount Azure share Notepad++'],  
            notify             => Package['Notepad++ (64-bit x64)']  
          }  
  
          exec { 'Remove Share Notepad++':  
            command  => "\$validate = (Test-Path -Path Z:); if (\$validate -eq \$true) {net use Z: /delete}",  
            provider => powershell,  
            before => File['Copy Notepad++ binary']  
          }  
        }  
        # Private cloud location  
        else {  
        exec { 'Get Notepad++ Nexus':  
        command  => "Invoke-WebRequest -Uri http://nexus_fqdn/${notepadplus} -OutFile C:\\Windows\\Temp\\${notepadplus}",  
        provider => powershell,  
        before   => Package['Notepad++ (64-bit x64)']  
        }  
      }  
  
      package { 'Notepad++ (64-bit x64)':  
        ensure          => present,  
        source          => "C:\\Windows\\Temp\\${notepadplus}",  
        install_options => ['/S']  
      }  
  
      file { 'Remove Notepad++ source':  
        ensure    => 'absent',  
        path      => "C:\\Windows\\Temp\\${notepadplus}",  
        subscribe => Package['Notepad++ (64-bit x64)']  
      }  
    }  
  }  
}  

 

As you can see, even if you don’t know the Puppet language, it’s not hard to read what is being done. This links with what was mentioned before, that now the code is the documentation of how to configure that machine, as all you need to do is look at the enabled modules and easily read them to understand what is supposed to be running. 

For more references on the language, check their site. 

 

Wrapping up

 

So, now the presence of Notepad++ on the server is managed and if it’s manually removed, the Puppet agent will enforce it and reinstall the application. And the same is also valid, if we specify that the application shouldn’t be installed (changing the package ensure option to absent), if you manually install it, Puppet will force its removal. And the same is valid for other components, like services and system/application configurations. 

 

This behaviour, in turn, also implies a shift in behaviour and process, since now we should use Puppet to manage the configurations on our servers. If we want to install, remove or update, it should be done via Puppet so we can ensure their correct application and enforcement (helping to minimize the configuration drift). 

 

Figure 4: CM software workflow

 

Another advantage, as also mentioned before, is the quality assurance of our code. Besides versioning, which would allow us to quickly roll-back a configuration, we can implement full CI/CD pipelines and have the code being applied on our machines fully validated and even tested in non-production environments 

 

Imagine you have a pool of servers configured with Puppet already, but you need to change some configurations or how an application is managed; since it’s code, you can have it run on a test server to guarantee that the changes done won’t break the production application. And after the test, feel safe to apply in production since it’ll run the same way, indifferent of on how many servers you run it. 

 

Now, the main drawback of this type of solutions is the implementation phase. It is a bit scary thinking about translating to code all the actions we’ve always done, via a script, CLI or point-and-click, mainly due to the size of the task. And it’ll take some time to learn and create/adapt the modules for all the applications and configurations on our managed systems, but it’s guaranteed that it will pay off, as the management, recovery, update or horizontal scaling of our systems is much easier, documented, tested and reproducible. 

 

As in the linear example I gave at the beginning, before we would have to spend days or weeks to deploy an entire pool of servers (with all the small mistakes and slight configuration changes due to human error), and repeat this every time we had to recover or upgrade our systems; now we just worry about the eventual OS install and then run Puppet… doing parallel configurations on multiple servers, always with the same result. 

 

Go back to the blog posts list