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 allow you to select relevant pieces of code from modules and bundle them together to create your own custom set of code for managing things. Profiles are the individual bundles of code. Roles gather profiles together so you can assign them to nodes. This allows you to efficiently organize your Puppet code.

To illustrate roles and profiles, these steps demonstrate how to:
  • Define a profile that configures the website and includes a firewall rule.
    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 (both IIS and the firewall) to accomplish a task.
  • Create a role to contain the profile.
  • Assign the role to the iis node group.
This creates a base structure where, if you had additional websites to serve, you would create additional profiles for those sites. When you have multiple profiles, you can combine profiles within roles or create unique roles for each profile.
Because this example adds a firewall rule, make sure you add the puppet/windows_firewall module to your Puppetfile, following the same process you used to Install the iis module. Remember to add the firewall modules dependencies (puppetlabs/stdlib and puppetlabs/registry), such as:
mod 'puppet/windows_firewall', '2.0.2'
mod 'puppetlabs/stdlib' , '4.6.0'
mod 'puppetlabs/registry' , '1.1.1'

Set up your prerequisites

Before 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 control repo named site. For example, /etc/puppetlabs/code/environments/production/site.
  3. Add site to the modulepath in the environment.conf file. The modulepath 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:
  • Installed the puppetlabs/iis module, the puppet/windows_firewall module, and their dependencies from the Forge.
  • Created the role and profile modules, as explained in Set up your prerequisites.
Tip: We recommend writing your code in a code editor, such as VSCode, and then pushing to your Git server. There is a Puppet VSCode extension that supports syntax highlighting of the Puppet language.
  1. In the profile module, create the following directories and .pp file:
    • manifests/
      • webserver/
        • example.pp
  2. Paste this Puppet code into the 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         => [
          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 the site's main page has content. Finally, there is a firewall rule that only allows traffic from the ports set in the $port setting.

    You can add your own code to the profile as needed. For more information, go to these Forge pages:

Set data for the profile

Hiera is a configuration method that allows you to set defaults in your code or override defaults (in certain circumstances). Use it to refine profile data.

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 consisting of:
  • 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 make webservers in the development environment have a custom message and 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"
 - '8080'
You'd use this code to make webservers in the production environment listen to all interfaces:
# /etc/puppetlabs/code/environments/production/data/stage/prod.yaml
 - ''
 - '::'

This is ta brief introduction to what 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

Roles contain sets of profiles. To write roles, think about the machines you're managing and decide what else they need in addition to the webserver profile.

This example shows how to write a role by combining profiles. In this example, assume you want all nodes in your iis node group to use the webserver profile you just wrote, and that your organization assigns all machines (including workstations) a profile called profile::base that manages basic policies and uses some conditional logic to include operating-system-specific configuration.
  1. In your control repo, open the .pp file for the role module. If it doesn't exist, create the necessary directories and file, such as:
  2. Write a role that includes both the base profile and your webserver profile:
    class role::exampleserver {
      include profile::base
      include profile::webserver
  3. 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 the node group containing the nodes that you want to have the iis_site configuration you wrote in the webserver::example profile.

For this example, assume you want to add role::exampleserver to all nodes in the iis node group.
  1. In the console, click Node groups and select the iis node group.
  2. On the Classes tab, select role::exampleserver and click Add class.
  3. Commit the change.
Now, the iis node group manages your iis_site website based on the rules you wrote in your webserver profile. When the nodes check in with PE, PE distributes the role (and the contained profiles) to the individual nodes and ensures the individual nodes have the IIS service and the desired configurations.