Dynamic Web Server Configuration with SaltStack Grains

SaltStack Grains provide static information about minions, making them invaluable for creating dynamic and adaptable configurations. This data, collected directly from the minion, can include details about the operating system, network interfaces, CPU architecture, memory, and much more. Leveraging Grains allows administrators to tailor configurations based on the specific characteristics of each target system without manual intervention.

Consider a scenario where Apache web servers need to be configured to listen only on their primary local IP address. Since IP addresses vary across servers, hardcoding them in configuration files is impractical. Grains offer a clean solution by providing the minion's network details, which can then be injected into configuration templates.

Inspecting Minion Grains

To undertsand the available data, you can list all grains reported by a minion using the grains.items module:

salt 'minion-01' grains.items

This command returns a comprehensive dictionary of all identified grains. For example, to retrieve specific information like total memory or the operating system:

salt 'minion-01' grains.item mem_total os

Output might look like:

minion-01:
    ----------
    mem_total:
        7812
    os:
        CentOS

Targeting Minions Using Grains

Grains are also powerful for targeting. You can selectively execute commands or apply states to minions that match specific grain criteria. For instance, to check the uptime of all CentOS machines:

salt -G 'os:CentOS' cmd.run 'uptime'

Accessing Network Interface IPs

A common requirement is to retrieve the IP address assigned to a minion. The fqdn_ip4 grain provides a list of IPv4 addresses associated with the fully qualified domain name. This is useful for binding services to a specific local IP.

salt '*' grains.item fqdn_ip4

Example output:

server-a:
    ----------
    fqdn_ip4:
        - 192.168.1.101
server-b:
    ----------
    fqdn_ip4:
        - 192.168.1.102

Implementing Dynamic Apache Binding with Grains

Let's create a Salt state to deploy and configure Apache, making it listen on the minion's primary IPv4 address retrieved via Grains. This involves using a Jinja templated configuration file.

First, define your Salt states in an SLS file (e.g., /srv/salt/apache/init.sls):

# /srv/salt/apache/init.sls

apache_installation:
  pkg.installed:
    - pkgs:
      - httpd
      - php
      - php-mysql

main_apache_config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd.conf.jinja
    - user: root
    - group: root
    - mode: '0644'
    - template: jinja
    - defaults:
        HTTP_PORT: 80
        BIND_IP: {{ grains['fqdn_ip4'][0] }}
    - require:
      - pkg: apache_installation

apache_auth_tools:
  pkg.installed:
    - name: httpd-tools

apache_authentication_file:
  cmd.run:
    - name: htpasswd -bc /etc/httpd/conf/htpasswd_file webadmin securepassword
    - unless: test -f /etc/httpd/conf/htpasswd_file
    - require:
      - pkg: apache_auth_tools

extra_apache_configs:
  file.recurse:
    - name: /etc/httpd/conf.d
    - source: salt://apache/files/conf.d
    - require:
      - pkg: apache_installation

php_configuration_file:
  file.managed:
    - name: /etc/php.ini
    - source: salt://apache/files/php.ini
    - user: root
    - group: root
    - mode: '0644'
    - require:
      - pkg: apache_installation

apache_service_management:
  service.running:
    - name: httpd
    - enable: True
    - reload: True
    - watch:
      - file: main_apache_config
      - file: extra_apache_configs
      - file: php_configuration_file

Next, create the Jinja template for httpd.conf (e.g., /srv/salt/apache/files/httpd.conf.jinja), where the BIND_IP and HTTP_PORT variables will be dynamically inserted:

# Snippet from httpd.conf.jinja

# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
Listen {{ BIND_IP }}:{{ HTTP_PORT }}

# Other Apache configurations...

Ensure you also have the placeholder files for php.ini and conf.d directory within files if they are managed by the state, e.g., /srv/salt/apache/files/php.ini and /srv/salt/apache/files/conf.d/some_config.conf.

Applying the State

To deploy this configuration to all minions, you can use state.apply. If this state is included in your top.sls, you would use state.highstate.

salt '*' state.apply apache

This command will ensure Apache is installed, configured to listen on the minion's specific IP address, and the service is running and properly managed.

Tags: SaltStack Grains apache Jinja Configuration Management

Posted on Wed, 21 Oct 2026 16:30:36 +0000 by cac_azure03