Organize webserver configurations with roles and profiles

The roles and profiles method is a reliable way to build reusable, configurable, and refactorable system configurations.

Roles and profiles help you pick out relevant pieces of code from modules and bundle them together to create your own custom set of code for managing things. Profiles are the actual bundles of code. Roles gather profiles together so that you can assign them to nodes. This allows you to efficiently organize your Puppet code.

To learn about roles and profiles by example, follow these instructions to define a profile that configures the example.com website and includes a firewall rule. Then, you will create a role to contain the profile, and you'll assign it to a the iis node group that you created earlier. This lays down a base structure where, if you had additional websites to serve, you would create additional profiles for them, and those profiles could be separated or combined inside the roles as needed.

Note: Adding a firewall rule isn’t necessary for an IIS website because the port is already open, but the purpose of this example is to show that you can write a role that manages more than one piece of software to accomplish a task. The webserver role can manage both IIS and the firewall.

Because you are adding a firewall rule, make sure you add the puppet/windows_firewall module to your Puppetfile, following the same process you used to add the iis module. Remember to add the firewall modules dependencies — puppetlabs/stdlib and puppetlabs/registry.

mod 'puppet/windows_firewall', '2.0.2'
mod 'puppetlabs/stdlib' , '4.6.0'
mod 'puppetlabs/registry' , '1.1.1'

See The roles and profiles method for more context on how roles and profiles work.

Set up your prerequisites

Before you begin writing content for roles and profiles, you need to create modules to store them in.

  1. Create one module for profile and one for role directly in your control repo. Do not put them in your Puppetfile.
  2. Make a new directory in the repo named site. For example, /etc/puppetlabs/code/environments/production/site.
  3. Edit the environment.conf file to add site to the modulepath. This is the place where Puppet looks for module information. For example: modulepath = site:modules:$basemodulepath.
  4. Put the role and profile modules in the site directory.

Write a profile for your IIS website

Write a webserver profile that includes rules for your iis_site and firewall.

Before you begin
Make sure you have already installed the puppetlabs/iis module and the puppet/windows_firewall module from the Forge.
Note: We recommend writing your code in a code editor, such as VSCode, and then pushing to your Git server. Puppet has an extension for VSCode that supports syntax highlighting of the Puppet language.
  1. In the profile module you added, create the following directory:
    • manifests/
      • webserver/
        • example.pp
  2. Paste the following Puppet code into the new example.pp file:
    class profile::webserver::example (
      String $content  = 'Hello from iis',
      String $port = '80',
    )
    {
    
      windows_firewall::exception { 'http':
        ensure       => present,
        direction    => 'in',
        action       => 'allow',
        enabled      => true,
        protocol     => 'TCP',
        local_port   => Integer($port),
        remote_port  => 'any',
        display_name => 'IIS incoming traffic HTTP-In',
        description  => "Inbound rule for IIS web traffic. [TCP ${port}]",
      }
    
      $iis_features = ['Web-WebServer','Web-Scripting-Tools', 'Web-Mgmt-Console']
      iis_feature { $iis_features:
        ensure => 'present',
      }
    
      # Delete the default website to prevent a port binding conflict.
      iis_site {'Default Web Site':
        ensure  => absent,
        require => Iis_feature['Web-WebServer'],
      }
    
      iis_site { 'minimal':
        ensure          => 'started',
        physicalpath    => 'c:\\inetpub\\minimal',
        applicationpool => 'DefaultAppPool',
        bindings        => [
          {
            'bindinginformation' => "${facts['ipaddress']}:${port}:",
            'protocol'           => 'http',
          }
        ],
        require         => [
          File['minimal-index'],
          Iis_site['Default Web Site']
        ],
      }
    
      file { 'minimal':
        ensure => 'directory',
        path   => 'c:\\inetpub\\minimal',
      }
    
      file { 'minimal-index':
        ensure  => 'file',
        path    => 'c:\\inetpub\\minimal\\index.html',
        content => $content,
        require => File['minimal']
      }
    }

    This profile applies custom rules for the iis_site class that include settings for $port and $content. The code uses file to ensure there is content on the main page of your site. Finally, there is a firewall rule that only allows traffic from the port or ports set in the $port setting.

    You can add your own code to the profile as needed. Look at the Readme and Reference sections for the

    puppetlabs/iis and puppet/windows_firewall modules in the Forge for more content.

Set data for the profile

Hiera is a configuration method that allows you to set defaults in your code, or override those defaults in certain circumstances. Use it to fine-tune data within your profile.

Suppose you want to use the custom fact stage to represent the deployment stage of the node, which can be dev, test, or prod. For this example, use dev and prod.

With Hiera structured data, you can set up a four-layer hierarchy that consists of the following:
  • console_data for data defined in the console.
  • nodes/%{trusted.certname} for per-node overrides.
  • stage/%{facts.stage} for setting stage-specific data.
  • common for global fallback data.

This structure lets you tune the settings for ports and IPs in each stage.

For example, to configure webservers in the development environment to have a custom message and to use port 8080, you'd create a data file with the following name, location, and code content:

# /etc/puppetlabs/code/environments/production/data/stage/dev.yaml
---
profile::webserver::example::content: "Hello from dev"
profile::webserver::example::ports:
 - '8080'

To have webservers in the production environment listen to all interfaces:

# /etc/puppetlabs/code/environments/production/data/stage/prod.yaml
---
profile::webserver::example::ips:
 - '0.0.0.0'
 - '::'

This is the briefest of introductions to all the things you can do with structured data in Hiera. To learn more about setting up hierarchical data, see Getting started with Hiera.

Write a role for your IIS website

To write roles, think about what machines you'll be managing and decide what else they need in addition to the webserver profile.

Say you want all of the nodes in your iis node group to use the profile you just wrote. Suppose also that your organization assigns all machines, including workstations, with a profile called profile::base, which manages basic policies and uses some conditional logic to include operating-system-specific configuration.

Inside the following file in your control repo:
site-modules\role\manifests\exampleserver.pp

Write a role that includes both the base profile and your webserver profile:

class role::exampleserver {
    include profile::base
    include profile::webserver
  }

You can add more profiles to this role, or create additional roles with more profile configurations based on your needs.

Assign the role to nodes

Assign the exampleserver role to nodes where you want to manage the configuration of the iis_site, based on what you wrote in the webserver::example profile.

For this example, assume you want to add role::exampleserver to all the nodes in the iis node group you created.

  1. In the console, click Classification and select the iis node group.
  2. On the Configuration tab, under Add a new class, select role::exampleserver and click Add class.
  3. Commit the change.
Result:
Your iis node group is managing your iis_site website based on the rules you coded into your webserver profile.