F30
F30

Reputation: 1192

Making a Salt `onchanges` requisite dependent on what has changed

I want to execute a Salt state not always when changes happened in another state, but only for specific changes. This appears like I would have to make onchanges/onchanges_in dependent on the specific changes.

The respective bug report has been closed saying "this is totally resolved now that states have access to the running dict and the lowstate for a state run". However, I can find no documentation on that and hardly any explanation of what the "running dict" actually is.

So I guess the question could also be rephrased as "How do I access the 'running dict' in an onchanges requisite?", but I'm open to any solutions for the original problem. Thanks for your help!


Update: A comment asked for a specific example, so here is my use case: As most state modules, user.present may either update fields of an existing (user) object or create a new one. Then, I want to run a second state module if and only if a specific field has been changed and/or the object has just been created. In Ansible, for comparison, I would register a variable and access the module's result through it.

So, why would I want to do that?

Essentially, I want to create user accounts on Linux and have them be able to set their own password (when logged in via an SSH key). user.present supports empty_password for that purpose, but it doesn't play nicely with enforce_password. This means that after a password has been manually set, a repeated state run will clear that password again. One might even consider this a bug in Salt, but the interactions between the different user.present fields are convoluted and debatable.

My solution is to create the accounts first and run a module.run state executing shadow.del_password afterwards. This is realised through an onchanges_in requisite. However, password deletion should not be triggered for any change, but only when the user account is created, which is also the only case my user.present state touches the password at all. Otherwise, things like adding users to a group would clear their password. For that effect, I think I would have to look into the details of the user.present change.

Create user account for dummy:
  user.present:
    - name: dummy
    - gid_from_name: True
    - remove_groups: False
    # TODO: This should be made more specific
    - onchanges_in:
      - module: Allow dummy to set a password

Allow dummy to set a password:
  module.run:
    - name: shadow.del_password
    - m_name: dummy
    # Make sure that this is not executed accidentally if no `onchanges_in` is present
    - onchanges: []
    - require:
      - user: Create user account for dummy

Upvotes: 1

Views: 4106

Answers (2)

alexK
alexK

Reputation: 953

I think what you want to use in this case is module.wait, not module.run. module.wait by default will not do anything, unless asked by something else. Also, onchanges_in for some reason (I think this issue) doesn't play well with module.wait for me. I've tried watch_in and it did the job.

I've tried the following code and it seem to work just fine. It creates a user with an empty password and doesn't change anything if user is already there:

Create user account for dummy:
  user.present:
    - name: dummy
    - gid_from_name: True
    - remove_groups: False
    # TODO: This should be made more specific
    - watch_in:
      - module: Allow dummy to set a password

Allow dummy to set a password:
  module.wait:
    - name: shadow.del_password
    - m_name: dummy
    - require:
      - user: Create user account for dummy

Upvotes: 0

Christophe Drevet
Christophe Drevet

Reputation: 945

I don't know about specific onchanges or the 'running dict', but, for your particular use case, you can use a condition to enable your password clearing state only when needed, such as:

Create user account for dummy:
  user.present:
    - name: dummy
    - gid_from_name: True
    - remove_groups: False

{% if salt['user.info']('dummy') == {} %}
# Only clear the password if the account didn't exist before
Allow dummy to set a password:
  module.run:
    - name: shadow.del_password
    - m_name: dummy
    - require:
      - user: Create user account for dummy
{% endif %}

Upvotes: 2

Related Questions