Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set register a variable to persist between plays in ansible?

I have an ansible playbook, where I'd like a variable I register on one machine to be available on another.

In my case, I'd like to run a command on localhost, in this case git rev-parse --abbrev-ref HEAD, so I can make a note of the current git branch, and sha1, and register this output, so I can refer to it later when working any machine in the main group, in the second play.

However, it's not clear to me how I register a variable on localhost, so I can access it from main. When I try to access the variable in the second play I get this message:

    TASK: [debug msg={{ app_git_sha1.stdout }}] ***********************************     fatal: [main] => One or more undefined variables: 'app_git_sha1' is undefined 

Here's the play I'm using. Is there anything obvious I should be doing?

    ---     - hosts: localhost       connection: local       gather_facts: no       tasks:         - name: register current branch           command: git rev-parse --abbrev-ref HEAD           register: git_branch           sudo: no           when: vagrant           tags:             - debugsha          - debug: msg={{ git_branch.stdout }}           tags:             - debugsha          - name: register the SHA1 of the branch being deployed           command: git rev-parse origin/{{ git_branch.stdout }}           register: app_git_sha1           sudo: no           tags:             - slack             - debugsha          - debug: msg={{ app_git_sha1.stdout }}           tags:             - debugsha        - hosts: main       sudo: yes       roles:         - role: productscience.deploy_user         # TODO reprovision using these roles, for consistency         # - role: app.essentials         # - role: zenoamaro.postgresql         - role: productscience.papertrailapp         - role: jdauphant.nginx       tasks:         - include: setup.yml         # - include: db.yml          - name: checkout source control when deploying to remote servers           include: source.yml           when: not vagrant           tags:               - deploy          - include: django.yml           tags:               - deploy           - name: include vagrant specific dependencies for local development           include: vagrant.yml           when: vagrant        handlers:         - name: restart postgres           sudo: yes           service: name=postgresql state=restarted         - name: start restart uwsgi           sudo: yes           service: name={{ app }} state=restarted      - hosts: localhost       connection: local       gather_facts: no       tasks:         - name: register the SHA1 of the branch being deployed           when: not vagrant           command: git rev-parse origin/{{ git_branch }}           register: git_sha           tags:             - slack          - name: Send notification message via Slack all options           when: not vagrant           tags:             - slack           local_action:             module: slack             token: "{{ wof_slack_token }}"             msg: "Deployment of `{{ git_branch }}` to {{ app_url }} completed with sha `{{ git_sha.stdout }}`"             channel: "#wof"             username: "Ansible deploy-o-tron" 
like image 727
Chris Adams Avatar asked Nov 24 '15 14:11

Chris Adams


People also ask

How do you use the register value as a variable in Ansible?

Ansible registers are used when you want to capture the output of a task to a variable. You can then use the value of these registers for different scenarios like a conditional statement, logging etc. The variables will contain the value returned by the task. The common return values are documented in Ansible docs.

What is register keyword in Ansible?

register − The output of the action is registered using the register keyword and Output is the variable name which holds the action output. always − Again a Ansible keyword , it states that below will always be executed. msg − Displays the message.

What is variable precedence in Ansible?

Ansible has a precedence for loading variable data, and thus it has an order and a definition to decide which variable will win. Variable value overriding is an advanced usage of Ansible, so it is important to fully understand the semantics before attempting such a scenario.


2 Answers

The problem you're running into is that you're trying to reference facts/variables of one host from those of another host. You need to keep in mind that in Ansible, the variable app_git_sha1 assigned to the host localhost is distinct from the variable app_git_sha1 assigned to the host main or any other host. If you want to access one hosts facts/variables from another host then you need to explicitly reference it via the hostvars variable. There's a bit more of a discussion on this in this question.

Suppose you have a playbook like this:

- hosts: localhost   tasks:        - command: /bin/echo "this is a test"       register: foo   - hosts: localhost   tasks:     - debug: var=foo 

This will work because you're referencing the host localhost and localhosts's instance of the variable foo in both plays. The output of this playbook is something like this:

PLAY [localhost] **************************************************************  GATHERING FACTS *************************************************************** ok: [localhost]  TASK: [command /bin/echo "this is a test"] ************************************ changed: [localhost]  PLAY [localhost] **************************************************************  GATHERING FACTS *************************************************************** ok: [localhost]  TASK: [debug var=foo] ********************************************************* ok: [localhost] => {     "var": {         "foo": {             "changed": true,             "cmd": [                 "/bin/echo",                 "this is a test"             ],             "delta": "0:00:00.004585",             "end": "2015-11-24 20:49:27.462609",             "invocation": {                 "module_args": "/bin/echo \"this is a test\"",                 "module_complex_args": {},                 "module_name": "command"             },             "rc": 0,             "start": "2015-11-24 20:49:27.458024",             "stderr": "",             "stdout": "this is a test",             "stdout_lines": [                 "this is a test"             ],             "warnings": []         }     } } 

If you modify this playbook slightly to run the first play on one host and the second play on a different host, you'll get the error that you encountered. The solution is to use Ansible's built-in hostvars variable to have the second host explicitly reference the first hosts variable. So modify the first example like this:

- hosts: localhost   tasks:      - command: /bin/echo "this is a test"       register: foo   - hosts: anotherhost   tasks:     - debug: var=foo       when: foo is defined      - debug: var=hostvars['localhost']['foo']       when: hostvars['localhost']['foo'] is defined 

The output of this playbook shows that the first task is skipped because foo is not defined by the host anotherhost. But the second task succeeds because it's explicitly referencing localhosts's instance of the variable foo:

TASK: [debug var=foo] ********************************************************* skipping: [anotherhost]  TASK: [debug var=hostvars['localhost']['foo']] ************************** ok: ['anotherhost'] => {     "var": {         "hostvars['localhost']['foo']": {             "changed": true,             "cmd": [                 "/bin/echo",                 "this is a test"             ],             "delta": "0:00:00.005950",             "end": "2015-11-24 20:54:04.319147",             "invocation": {                 "module_args": "/bin/echo \"this is a test\"",                 "module_complex_args": {},                 "module_name": "command"             },             "rc": 0,             "start": "2015-11-24 20:54:04.313197",             "stderr": "",             "stdout": "this is a test",             "stdout_lines": [                 "this is a test"             ],             "warnings": []         }     } } 

So, in a nutshell, you want to modify the variable references in your main playbook to reference the localhost variables in this manner:

{{ hostvars['localhost']['app_git_sha1'] }} 
like image 132
Bruce P Avatar answered Oct 20 '22 11:10

Bruce P


Use a dummy host and its variables

For example to pass K8S token and hash from the master to the workers.

On master

- name: "Cluster token"   shell: kubeadm token list | cut -d ' ' -f1 | sed -n '2p'   register: K8S_TOKEN  - name: "CA Hash"   shell: openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'   register: K8S_MASTER_CA_HASH  - name: "Add K8S Token and Hash to dummy host"   add_host:     name:   "K8S_TOKEN_HOLDER"     token:  "{{ K8S_TOKEN.stdout }}"     hash:   "{{ K8S_MASTER_CA_HASH.stdout }}"  - name:   debug:     msg: "[Master] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}"  - name:   debug:     msg: "[Master] K8S_TOKEN_HOLDER K8S Hash is  {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" 

On worker

- name:   debug:     msg: "[Worker] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}"  - name:   debug:     msg: "[Worker] K8S_TOKEN_HOLDER K8S Hash is  {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}"  - name: "Kubeadmn join"   shell: >     kubeadm join --token={{ hostvars['K8S_TOKEN_HOLDER']['token'] }}     --discovery-token-ca-cert-hash sha256:{{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}     {{K8S_MASTER_NODE_IP}}:{{K8S_API_SERCURE_PORT}} 
like image 22
mon Avatar answered Oct 20 '22 09:10

mon