Search code examples
macrosenvironment-variablessalt-project

SaltStack macros and environment variables


I'm trying to create a formula to manage MySQL accounts on multiple database servers. I'm trying to re-use a macro from a macro.sls file, and use environment variables to specify connection settings. However, putting environment files in the state files for each database server doesn't work; it only works when I put them in the macro.sls file. This is not desired since I want to be able to update users on multiple database servers at once. See below:

# Example of "db_user" macro in mysql_user-mgmt/macro.sls
{%- macro db_user(user, password_hash, require_state="None") %}
{{ user }}:
  mysql_user.present:
    - connection_charset: {{ db_charset }}
    - connection_host: {{ db_host }}
    - connection_pass: {{ db_pass }}
    - connection_port: {{ db_port }}
    - connection_user: {{ db_user }}
    - host: "%"
    - password_hash: "{{ password_hash }}"
  {%- if require_state != "None" %}
    - require:
      - {{ require_state }}
  {%- endif %}
{%- endmacro %}

Example db-server1.sls state file using macro

# My mysql_user-mgmt/db-server1.sls file
{%- import "mysql_user-mgmt/macro.sls" as macro -%}
{%- set db_charset = salt['environ.get']('SERVER1_DB_CHAR') -%}
{%- set db_host    = salt['environ.get']('SERVER1_DB_HOST') -%}
{%- set db_pass    = salt['environ.get']('SERVER1_DB_PASS') -%}
{%- set db_port    = salt['environ.get']('SERVER1_DB_PORT') -%}
{%- set db_user    = salt['environ.get']('SERVER1_DB_USER') -%}

{{ macro.db_user("someUser", "<MYSQL HASHED PASSWORD>") }}

...and the output then I try to run the salt-call state.sls mysql_user-mgmt.db-server1 command

# Output of running `salt-call state.sls mysql_user-mgmt.db-server1`
local:
    - Rendering SLS 'base:mysql_user-mgmt.db-server1' failed: Jinja variable 'db_charset' is undefined
      /var/cache/salt/minion/files/base/mysql_user-mgmt/macro.sls(4):
      ---
      {%- macro db_user(user, password_hash, require_state="None") %}
      {{ user }}:
        mysql_user.present:
          - connection_charset: {{ db_charset }}    <======================
          - connection_host: {{ db_host }}
          - connection_pass: {{ db_pass }}
          - connection_port: {{ db_port }}
          - connection_user: {{ db_user }}
          - host: "%"
      [...]
      ---

Solution

  • I believe what you should do is to pass variables as a parameters to the marco like this:

    {%- macro db_user(
       user,
       password_hash,
       db_charset,
       db_host,
       db_pass,
       db_port,
       db_user,
       require_state="None"
    ) %}
    {{ user }}:
      mysql_user.present:
        - connection_charset: {{ db_charset }}
        - connection_host: {{ db_host }}
        - connection_pass: {{ db_pass }}
        - connection_port: {{ db_port }}
        - connection_user: {{ db_user }}
        - host: "%"
        - password_hash: "{{ password_hash }}"
      {%- if require_state != "None" %}
        - require:
          - {{ require_state }}
      {%- endif %}
    {%- endmacro %}
    

    and then call a macro like this:

    {%- import "mysql_user-mgmt/macro.sls" as macro -%}
    {%- set db_charset = salt['environ.get']('SERVER1_DB_CHAR') -%}
    {%- set db_host    = salt['environ.get']('SERVER1_DB_HOST') -%}
    {%- set db_pass    = salt['environ.get']('SERVER1_DB_PASS') -%}
    {%- set db_port    = salt['environ.get']('SERVER1_DB_PORT') -%}
    {%- set db_user    = salt['environ.get']('SERVER1_DB_USER') -%}
    {{ macro.db_user(
       "someUser",
       "<MYSQL HASHED PASSWORD>",
       "db_charset",
       "db_host",
       "db_pass",
       "db_port",
       "db_user"
    ) }}
    

    I'm writing this code from my head - so there might be some errors in syntax, but the logic should be correct.