Buy

pre_tasks and set_fact

I want to guarantee that the symfony_env variable is always lowercased so that I can safely use. A cool way to do this is with a "pre task".

Add a new key called... well... pre_tasks and a new task called "Convert entered Symfony environment to lowercase":

228 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
pre_tasks:
- name: Convert entered Symfony environment to lowercase
... lines 22 - 26
tasks:
... lines 28 - 228

Pre-tasks are exactly like tasks... they just run first.

Wait... then what the heck is the difference between a "pre task" and just putting the task at the top of the task section?

Nothing! Well, nothing yet. But later, when we talk about roles - ooh roles are fancy - there will be a difference: pre tasks run before roles.

Anywho, in this pre task, we basically want to re-set the symfony_env variable to a lowercased version of itself. To do that, we'll use a new module: set_fact. We already know that we can set variables in 3 different ways: with vars, vars_prompt or by using the register key below a task. The set_fact module is yet another way.

Facts versus Variables

But wait... why set_fact and not set_variable? So... here's the deal: you'll hear the words "facts" and "variables" in Ansible... basically interchangeably. Both facts and variables can be set, and are referenced in exactly the same way. And while there do seem to be some subtle differences between the two, if you think of a fact and a variable as the same thing, it'll make your life easier. When you run the playbook, the first task is called "setup", and it prepares some facts about the host machine. That's usually where you hear the word facts: it's info about each host.

So, we're using set_fact... to set a fact... or a variable... Set symfony_env to {{ symfony_env|lower }}:

228 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
pre_tasks:
- name: Convert entered Symfony environment to lowercase
set_fact:
symfony_env: "{{ symfony_env|lower }}"
... lines 24 - 228

Love it! Oh, and just like with tasks, these can be tagged. We'll probably want this to run all the time... so let's use a special tag called always:

228 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
pre_tasks:
- name: Convert entered Symfony environment to lowercase
set_fact:
symfony_env: "{{ symfony_env|lower }}"
tags:
- always
... lines 26 - 228

Try the playbook!

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini

Be difficult and use "PROD" in all capital letters again.

Ha, ha! This time, our playbook outsmarts us and lowercases the value.

Environment Variables and Pre Tasks

Remove the two debug tasks:

222 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 16
environment:
SYMFONY_ENV: "{{ symfony_env|lower }}"
... lines 19 - 26
tasks:
- ping: ~
... lines 29 - 222

Can we also remove the |lower from the environment variable? Actually... no! The environment section runs before tasks, even pre_tasks. And this has nothing to do with the order we have things in this file - environment always runs first. So, keep the |lower.

Using the symfony_env Variable

Ok, time to use our fancy symfony_env variable! First, when we install the composer dependencies, we currently have no_dev: no. But now, if the environment is prod, this can be yes. Let's use a fancy expression! {{ 'yes' if (prod == symfony_env) else 'no' }}:

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 165
- name: Install Composer's dependencies
composer:
... line 168
no_dev: "{{ 'yes' if ('prod' == symfony_env) else 'no' }}"
... lines 170 - 224

Don't forget your curly braces. Weird, but cool! This is a special Jinja syntax.

Conditionally Running Tasks

Next, find the fixtures task. Hmm. If we're deploying in the prod environment... we might not want to load the data fixtures at all. But so far, we don't have any way to conditionally run a task: tasks always run.

Well guess what? We can tell a task to not run with the when key. In this case, say when: 'symfony_env != "prod":

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 198
- name: Load data fixtures
... line 200
when: symfony_env != "prod"
... lines 202 - 224

Finally, down in the Clear cache task, instead of prod, use {{ symfony_env }}:

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 205
- name: Clear cache
command: '{{ symfony_console_path }} cache:clear --env={{ symfony_env }}'
... lines 208 - 224

Let's try this thing! Re-run the playbook, but use -t deploy:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini -t deploy

Use the prod environment. As we just saw, the vars_prompt runs always: it doesn't need a tag:

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 10
vars_prompt:
- name: symfony_env
... lines 13 - 224

Then, our "pre task" should run thanks to the always tag:

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 19
pre_tasks:
- name: Convert entered Symfony environment to lowercase
... lines 22 - 23
tags:
- always
... lines 26 - 224

By the time the "Composer Install" task executes, it should run with no_dev: yes, and then hopefully it'll skip data fixtures and change "Clear cache":

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 165
- name: Install Composer's dependencies
composer:
... line 168
no_dev: "{{ 'yes' if ('prod' == symfony_env) else 'no' }}"
... lines 170 - 198
- name: Load data fixtures
... line 200
when: symfony_env != "prod"
... lines 202 - 205
- name: Clear cache
command: '{{ symfony_console_path }} cache:clear --env={{ symfony_env }}'
... lines 208 - 224

The "Install Composer's dependencies" does show as changed: that's a good sign: it should have installed less packages than before. And yea! It's skipping the fixtures!

In the VM, try to ls vendor/sensio. Ok cool! One of the require-dev dependencies is sensio/generator-bundle. That is not here, proving that the dev dependencies did NOT install this time. We are in business!

And before we continue, under the "Clear Cache" task, add changed_when: false:

224 lines ansible/playbook.yml
---
- hosts: vb
... lines 3 - 26
tasks:
... lines 28 - 205
- name: Clear cache
... line 207
changed_when: false
... lines 209 - 224

That's not critical, it'll just prevent it from showing up as changed on every run.

Now, let's create a faster, smarter playbook by skipping some redundant tasks!

Leave a comment!