Input validation on user-supplied variable (example, setting environment to production/development/staging)

Good afternoon,

I opened an issue on GitHub, and was directed here, which is the obvious place. (I initially thought that the behavior was indeed a bug, but there was some clarity added).

What would be the preferred method to perform input validation on a user supplied variable provided via extra vars? Is the recommended approach to simply handle an error (and end the host, play or task) if the variable doesn’t match an expected input, as it is not possible to overwrite it due to precedence?

As it was, we were trying to use “-e” for the extra variables, and then using set_fact to overwrite it if it didn’t meet the expected requirements.

This is what I was trying:

`

###This works as expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment is undefined
  
  tags:
    - variable-test-task

###This does not work
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment != "Development"
  
  tags:
    - variable-test-task

###This does not work
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment == "Production"
  
  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: not (deploy_environment is defined) or not ((deploy_environment == 'Development') or (deploy_environment == 'Production'))
  
  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: (deploy_environment is undefined) or ((deploy_environment != 'Development') or (deploy_environment != 'Production'))
  
  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: (deploy_environment != 'Development') or (deploy_environment != 'Production')
  
  tags:
    - variable-test-task

`

And that was patiently explained to have been by design. I’m open to suggestions on the best overall way to accomplish this.

(Slightly related, I’d like to submit documentation changes that may make this more clear for users, and is that best done via a merge request?)

Thanks,
Michael

Hi

Good afternoon,

I opened an issue on GitHub, and was directed here, which is the obvious place. (I initially thought that the behavior was indeed a bug, but there was some clarity added).

What would be the preferred method to perform input validation on a user supplied variable provided via extra vars? Is the recommended approach to simply handle an error (and end the host, play or task) if the variable doesn't match an expected input, as it is not possible to overwrite it due to precedence?

As it was, we were trying to use "-e" for the extra variables, and then using set_fact to overwrite it if it didn't meet the expected requirements.

This is what I was trying:

###This works as expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment is undefined

  tags:
    - variable-test-task

###This does not work
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment != "Development"

  tags:
    - variable-test-task

###This does not work
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: deploy_environment == "Production"

  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: not (deploy_environment is defined) or not ((deploy_environment == 'Development') or (deploy_environment == 'Production'))

  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: (deploy_environment is undefined) or ((deploy_environment != 'Development') or (deploy_environment != 'Production'))

  tags:
    - variable-test-task

###This does not work, playing with syntax, might be expected
- name: "Setting Deploy Environment Variable for Deployment Tasks"
  set_fact:
    deploy_environment: "Development"

  when: (deploy_environment != 'Development') or (deploy_environment != 'Production')

  tags:
    - variable-test-task

And that was patiently explained to have been by design. I'm open to suggestions on the best overall way to accomplish this.

You're right about the precedence getting in the way of altering
variables set with '-e', that's indeed not possible.
However this is usually not a problem, as long as you use it to
override something that is set already.
But set_fact can't win from '-e', so that won't work.
There are a few alternatives.
I usually avoid having to change variables directly, and instead use
two variables. One meant for 'internal' use inside the role, which as
a convention, I prefix with an underscore.
For instance '_env'. And then have an 'external' var meant for command
line usage, say 'deploy_environment'. Then any logic with set_fact etc
will work.

But, looking at your code again - all you seem to want is to provide a
default for the 'deploy_environment' variable.
In that case, '-e' will just work fine, for instance:

- hosts: localhost
  connection: local
  gather_facts: no

  vars:
    deploy_environment: Development

  tasks:
    - debug:
        msg: |
          We are deploying the {{ deploy_environment }} environment now

If you run this without any '-e' switched, it will use Development.
And you can just override it with '-e deploy_environment=Production'.

For failing gracefully, look at the 'assert' module. The following
example ensure only valid environments are being used: