Thursday, February 11, 2016

Ansible Handlers


Ansible as said will perform many operations on remote machine and some need a recycle or stop of service when things change. Ansible identifies what changes are done but if the changes need to be effective there needs to be some post change events. Consider when a new virtual host information is added httpd.conf, there needs a restart of the httpd service.But the most important thing at this moment is tell Ansible to restart the service. This is done via the notify action, and tasks that are called by this notify action are called handlers. So when we tell Ansible to restart the httpd service (Called as to notify ) and the task that will care of this notify action is called as handler.

Every handler task will run at the end of the playbook for example, you changed your httpd server config multiple times and you want to restart the httpd service so that the changes are applied. Restarting a Service for every change is not a good practice rather we can notify Ansible to restart httpd service on every configuration change. Ansible will then make sure no matter how many times we notify if for the restart of the Httpd service ,the httpd service is restarted only once and this is done after all others tasks are complete

Let’s see a basic example of defining a Notify and Handler,

[root@vx111a ansible]# cat apache.yml
---
- hosts: dev
  user: root
  sudo: no
  tasks:
   - name: Install Apache
     yum: pkg=httpd state=latest
     notify:
     - restart apache
   - name: ensure apache is running
     service: name=httpd state=started 
  handlers:
   - name: restart apache
     service: name=httpd state=restarted

if you check the above playbook , we have defined a task to install Httpd Package and also added a notify to restart the service. The restart is then handled by the handlers at the end.
Upon running the playbook we can see

[root@vx111a ansible]# ansible-playbook apache.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Install Apache] ********************************************************
changed: [172.16.202.96]

TASK: [ensure apache is running] **********************************************
changed: [172.16.202.96]

NOTIFIED: [restart apache] ****************************************************
changed: [172.16.202.96]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=4    changed=3    unreachable=0    failed=0  

One important thing to note is that Handlers are triggered only when a task status is changed.


More to Come ,Happy learning 
Read More

Ansible – Include


We have how we can use variable which working with playbooks. However, it's not just variables that we include from other files; it can also be common tasks or handlers. This is a feature in Ansible to reduce duplicity while writing tasks. This also allows us to have smaller playbooks by including reusable code in separate tasks using Don't Repeat Yourself (DRY).

The below playbook is the most basic example of using the include construct

[root@vx111a 2test]# cat install_apache.yml
- set_fact: package_name=httpd
- name: install Apache
  yum: name=httpd state=present

In the above yml file, we have defines a variable “package_name” which was given a value of httpd. After that we are installing the Apache Package. Now we will use this yml file into our playbook as,

---
- hosts: dev
  tasks:
    - include: install_apache.yml
    - name: check Apache Service
      service: name={{ package_name }} state=restarted

If you check the above playbook, we can see that we have used a include element for including the install_apache.yml file. We also defined a task for restarting the httpd using the Service module. We have defined to access the package_name using the service: name={{ package_name }} state=restarted . Now lets run the playbook

[root@vx111a 2test]# ansible-playbook test.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [set_fact package_name=httpd] *******************************************
ok: [172.16.202.96]

TASK: [install Apache] ********************************************************
ok: [172.16.202.96]

TASK: [check Apache Service] **************************************************
changed: [172.16.202.96]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=4    changed=1    unreachable=0    failed=0  

We can see that the play book ran perfectly fine with accessing the variable name package_name from the install_apache.yml file that we included.

As said above, include not just allows variable files to be included but it also allows to include tasks and handlers. Lets see another example of using the Include. Lets see the basic playbooks

[root@vx111a include]# cat ./tasks/include_tasks.yml
---
- name: "{{ service_state }} {{ service_name }} is being started"
  service: name={{ service_name}} state={{ service_state }}


[root@vx111a include]# cat main.yml
---
- hosts: cent
  tasks:
    - include: tasks/include_tasks.yml service_name="httpd" service_state="stopped"

The include_tasks.yml is created under the tasks folder which contains a task with service module to stop the Httpd service. Now we will define another playbook file which includes the include_tasks.yml file as in main.yml. In the include_task.tml file I have not just defined the service but I also passed the service name and state as arguments. Now once we execute the playbook we see,

[root@vx111a include]# ansible-playbook main.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [stopped httpd is being started] ****************************************
ok: [172.16.202.96]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=0    unreachable=0    failed=0 

Hope this gives you a basic understating of using Include in Ansible
Read More

Ansible Loops


Unitl now we have seen how we can use with , in and not operators to perform basic check but what if we need some thing like executing the task multiple times. Consider a case where you want to create multiple users or want to install multiple packages. In a normall programming language we can achieve these using loops. Anisble does provide a similar feature.

Standard Loops
The standard loops allow passing a list so that Ansible will run the task for all packages listed. Let's now go through an example playbook.

[root@vx111a loops]# cat loop1.yml
---
- hosts: cent
  tasks:
 
  - name: Create Multiple users
    shell: useradd {{ item }}
    with_items:
       - hai
       - bai

This simple playbook will run the useradd command taking each from the list defined udner the with_items construct. Each of the names is exposed as item, which is a default variable that Ansible creates. Ansible then assigns a package name to item, based on the iteration it is currently part of.

Once we run the above playbook,

[root@vx111a loops]# ansible-playbook loop1.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Create Multiple users] *************************************************
changed: [172.16.202.96] => (item=hai)
changed: [172.16.202.96] => (item=bai)

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=1    unreachable=0    failed=0  
We can see that both users are created by using the loops in Ansible

Lets see another example of using the with_items construct with variable substitution.

[root@vx111a loops]# cat loop2.yml
---
- hosts: cent
  tasks:
 
  - name: Installing Packages
    yum: name={{item.name}} state={{item.value}}
    with_items:
       - {name: 'httpd', value: 'present'}

In the above playbook we have defined a task which installs packages that are defined in the with_items construct. We have defined the package name with name and value variable which are then accessed by the item element. The item does then have the name and value data which will substitute when needed. When we execute the playbook we see,

[root@vx111a loops]# ansible-playbook loop2.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Installing Packages] ***************************************************
changed: [172.16.202.96] => (item={'name': 'httpd', 'value': 'present'})

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=1    unreachable=0    failed=0  

Nested Loops
Nested loops are usefull when we want to perform multiple operations on the same resource. For example consider a case where we want to set different permission on a file or directory thus defining them using sub elements.

[root@vx111a loops]# cat nested_loop.yml
---
- hosts: cent
  tasks:
 
  - name: Nested Loop / Change File Permissions
    shell: chmod -R {{ item[0] }} {{ item[1] }}
    with_nested:
       - [ '755' ]
       - [ '/tmp/hai' ]

The above playbook is a simple example of nested loops. In the above one we have defined a task which will change the permission on a file /tmp/hai with 755. The values are defined with with_nested construct and can be accessed as an array access (a[0]). When we execute the above playbook,

 [root@vx111a loops]# ansible-playbook nested_loop.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Nested Loop / Change File Permissions] *********************************
changed: [172.16.202.96] => (item=['755', '/tmp/hai'])

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=1    unreachable=0    failed=0  

I will add a more sophisticated example for the nested loops

Sub Nested Loops
Till now we have seens loops with static data and what if we want give data dynamically. Consider an example of creating multiple users along with different comments. To deal with such cases, you can use a dictionary to loop over subelements. Using such loops, you can specify a set of comments per user. Consider a sample example as,

[root@vx111a loops]# cat subnested.yml
---
- hosts: cent
  vars:
    users:
     - name: jagadish
       comments:
         - 'Jagadish is Good'

     - name: srini
       comments:
         - 'Srini is Bad' 

  tasks:
   - name: User Creation
     shell: useradd -c "{{ item.1 }}" "{{ item.0.name }}"
     with_subelements:
         - users
         - comments

In the above playbook we defined a task which will create users along with user comments taken from 2 dictionaries users and comments. Both users and comments are defined in the vars element above. Both dictionaries are defined under the with_subelements construct making Ansible to get sub elements from users and comments for running the task. When we execute the playbook we can see,

[root@vx111a loops]# ansible-playbook subnested.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [User Creation] *********************************************************
changed: [172.16.202.96] => (item=({'name': 'jagadish'}, 'Jagadish is Good'))
changed: [172.16.202.96] => (item=({'name': 'srini'}, 'Srini is Bad'))

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=1    unreachable=0    failed=0  


That’s all about Loops. In the next article we will see more options provided by Ansible.
Read More

Ansible – more Operators


In the previous article we have seen how we can use the with condition to check for things. Ansible also provides in and not Operators for additional validations. In this article we will see how in and not can be used in our playbooks.

Some times we need to check only a certain part of the string or the output. To perform such kinds of checks, Ansible provides the in and not operators.

[root@vx111a with-ansible]# cat with2.yml
---
- hosts: cent
  tasks:
    - name: Check HTTPD Packages
      shell: rpm -qa  | grep httpd
      register: rpm_status
      ignore_errors: yes

    - name: check If HTTPD Rpm is Installed
      debug: msg="Httpd is installed on the remote Machine"
      when: " 'httpd' in rpm_status.stdout"

    - name: Check if Httpd is installed
      debug: msg="Httpd is not installed on the remote machine"
      when: not 'httpd' in rpm_status.stdout

the above one is the basic example of using the in and not operators. The playbook defines a task using a shell module to check for the httpd package. The task is registered with a variable rpm_status from which we extract data.

We use the rpm_status variable stdout to check whether the httpd is in the output using the in Operator. In the next task we use the not operator to check that the string httpd is not in the variable ouput. When we run the playbook we see

[root@vx111a with-ansible]# ansible-playbook with2.yml

PLAY [cent] *******************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Check HTTPD Packages] **************************************************
failed: [172.16.202.96] => {"changed": true, "cmd": "rpm -qa | grep httpd", "delta": "0:00:00.120259", "end": "2016-02-09 09:54:44.286588", "rc": 1, "start": "2016-02-09 09:54:44.166329", "warnings": ["Consider using yum module rather than running rpm"]}
...ignoring

TASK: [check If HTTPD Rpm is Installed] ***************************************
skipping: [172.16.202.96]

TASK: [Check if Httpd is installed] *******************************************
ok: [172.16.202.96] => {
    "msg": "Httpd is not installed on the remote machine"
}

PLAY RECAP ********************************************************************
172.16.202.96              : ok=3    changed=1    unreachable=0    failed=0  


More to Hope, Happy Learning J
Read More

Ansible Conditionals


In our previous article we have seen how playbooks work and how tasks defined in the playbooks are executed. But in all these cases we see that Ansible executes the tasks in sequentially. Ansible also provides us conditionals in executing tasks bases on conditions.

For example, let's say you have a playbook that will install Apache HTTPD Server on the remote host. Now, the Apache HTTPD Server has a different package name for a Debian-based operating system, and it's called apache2; for a Red-Hat-based operating system, it's called httpd. But defining multiple playbooks for different flavors of the *nix operating system is waste of time.

Ansible allows us to identify system and execute certain command based on this. Ansible provides conditional statements that help run a task only when a specified condition is met.Ansible provide a WHEN condition to execute tasks based on certain conditions. Lets see a basic example of usin When condition

[root@vx111a with-ansible]# cat with.yml
---
- hosts: dev
  tasks:
    - name: install httpd Package
      yum: name=httpd state=latest
      sudo: yes
      when: ansible_os_family == "Redhat"

    - name: install apache2 package
      apt: name=apache2 state=latest
      sudo: yes
      when: ansible_os_family == "Debian"

The above playbook is basic example of using the when condition. This is similar to if-else loop in the programming syntax. In the above playbook, we first check what the anisble_os_family is. Once we get the details we will then install the necessary Apache packages based on that.

In case if that is Redhat we will go with the HTTPD or apache2 if that is Debain.

Note – Every time Ansible runs a playbook it collects certain details about the remote machine called Facts. This can be disabled when we add the gather_facts: false in our playbook.  This gathers all the necessary details about the remote system including variables, memory and all necessary configurations that are set for the remote machine

The second task in the playbook will install the latest apache2 package if the ansible_os_family is Debian. If the when condition doesn't match a task, then it will simply skip that task and move forward.
Lets run the playbook

[root@vx111a with-ansible]# ansible-playbook with.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]
ok: [172.16.202.97]

TASK: [install httpd Package] *************************************************
skipping: [172.16.202.96]
skipping: [172.16.202.97]

TASK: [install apache2 package] ***********************************************
skipping: [172.16.202.96]
changed: [172.16.202.97]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=1    changed=0    unreachable=0    failed=0  
172.16.202.97              : ok=2    changed=1    unreachable=0    failed=0  

In the above case, the IP (172.16.202.97) is a Debain machine and the other one was a Centos machine.

Ansible allows a variable to register to task that we run on the remote machine. Lets see another example of using the with Condition along with register

[root@vx111a with-ansible]# cat with1.yml
---
- hosts: dev
  tasks:
    - name: Check for File
      command: /usr/bin/cat /tmp/hai
      register:  cat_response
      ignore_errors: yes

    - name: Stop Futher if File is Not available
      fail: msg="File is Not available, Please Create the File First"
      when: cat_response.rc == 2


In the above playbook we have defined a command to run on the remote machine. The command is to “cat /tmp/hai”. We will make sure to capture the output in the variable cat_response using the register module.

Ignore_errors – in some conditions when we run a playbook it may fail due to missing variables or packages or any other reason. What if we want to continue the playbook even though certain tasks defined were failed? . We can use the ignore_errors: yes Condition duing this case. The above condition only governs the return value of failure of the particular task, so if you have an undefined variable used, it will still raise an error that users will need to address. Neither will this prevent failures on connection nor must execution issues, the task be able to run and return a value of ‘failed’.

Now if you check the above playbook we have defined a task to check the Contents of the file /tmp/hai. If the file is not available the task fails and then we have set the ignore_error to yes which will ignore the errors and continue the playbook execution.

Every task that is run will be having a stdout and stderr. Besides these there will some other details too like return code (rc) of the command, command itself, start and end time of the command being executed etc.

- name: Stop Futher if File is Not available
      fail: msg="File is Not available, Please Create the File First"
      when: cat_response.rc == 2

In the above playbook we are checking the return code of the task executed and saved in the cat_response variable. We then used the when condition to make sure that the rc command is equal to 2 , then use the fail module to generate a failure message

Note - Apart from ==, Ansible also supports the >, <, <=, >=, and != operators with the when condition.

Now lets run the playbooks as,

[root@vx111a with-ansible]# ansible-playbook  with1.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]
ok: [172.16.202.97]

TASK: [Check for File] ********************************************************
failed: [172.16.202.97] => {"cmd": "/usr/bin/cat /tmp/hai", "failed": true, "rc": 2}
msg: [Errno 2] No such file or directory
...ignoring
failed: [172.16.202.96] => {"cmd": "/usr/bin/cat /tmp/hai", "failed": true, "rc": 2}
msg: [Errno 2] No such file or directory
...ignoring

TASK: [Stop Futher if File is Not available] **********************************
failed: [172.16.202.96] => {"failed": true}
msg: File is Not available, Please Create the File First
failed: [172.16.202.97] => {"failed": true}
msg: File is Not available, Please Create the File First

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/with1.retry

172.16.202.96              : ok=2    changed=0    unreachable=0    failed=1  
172.16.202.97              : ok=2    changed=0    unreachable=0    failed=1  

We can see fail message output when the playbook is ran. We can also see that though the task failed the execution continued and displayed messages accordingly.


I hope I have given clear examples of using the with Condition in Ansbile.
Read More

Ansible – Local Action


There are certain cases where we want tasks to run the same machine that runs the Ansible-playbook command. Consider a case where you want to run shell command on the local machine simultaneously when the playbook is running. Also we can make sure that certain tasks needs to be fulfilled in order to run the other parts of the playbook on remote machine. That is we can make sure that a file is available on local machine before running tasks on the remote machine.

The local_action option provided by Ansible comes into the picture in such situations.If you pass the module name and the module argument to local_action, it will run that module locally. Let’s see an example of using local_action in playbook,

[root@vx111a local_action]# cat local_action.yml
---
- hosts: dev
  tasks:
    - name: remote running process
      shell: ps
      register: remote_process

    - debug: msg="{{ remote_process.stdout }} "

    - name: Running Local Process
      local_action: shell ps
      register: local_process
  
    - debug: msg="{{ local_process.stdout }}"

The above is the playbook that I will be using for local_action. If we the above playbook we can see that we have 2 taks

- name: remote running process
   shell: ps
   register: remote_process

This task is for running PS ( shell ) command will be ran on the remote machine. A register remote_process was set for this to check the output

 - name: Running Local Process
   local_action: shell ps
   register: local_process

This is the second task that will run only on the local machine due to the local_action module that we specified. In order to run local actions we need to define the local_action module for the command or task to run.

When we execute the playbook we can see,

[root@vx111a local_action]# ansible-playbook local_action.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [remote running process] ************************************************
changed: [172.16.202.96]

TASK: [debug msg="{{ remote_process.stdout }} "] ******************************
ok: [172.16.202.96] => {
    "msg": "  PID TTY          TIME CMD\n12775 pts/1    00:00:00 sh\n12776 pts/1    00:00:00 python\n12777 pts/1    00:00:00 ps "
}

TASK: [Running Local Process] *************************************************
changed: [172.16.202.96 -> 127.0.0.1]

TASK: [debug msg="{{ local_process.stdout }}"] ********************************
ok: [172.16.202.96] => {
    "msg": "  PID TTY          TIME CMD\n24362 pts/0    00:00:02 bash\n30565 pts/0    00:00:00 ansible-playboo\n30587 pts/0    00:00:00 sh\n30588 pts/0    00:00:00 python\n30593 pts/0    00:00:00 ps"
}

PLAY RECAP ********************************************************************
172.16.202.96              : ok=5    changed=2    unreachable=0    failed=0  

We can see that both outputs are different as one ran on the Remote Machine and the other one on the local machine.

This helps in making sure that some things are available before running on remote machine.
Read More

Wednesday, February 3, 2016

Ansible Modules


Until now we have seen playbooks, variables and inventories we will now move to the modules. Ansible provides more than 200 modules under the top level modules like system, Network, Datbase, Files and many others.

Ansible comes with modules (also called as module library) that can be executed directly on remote hosts or through playbooks. Ansible allows users to write their own modules. These helps in performing various operations from starting services, installing packages to executing commands.

In this article we will see a couple of most used modules.

Command – Command module allows us to execute commands on the remote machine. A simple play book for the Command module is,

[root@vx111a inventory]# cat command.yml
---
- hosts: dev
  tasks:
   - name: Check for Command ls
     command: /bin/echo Hello

Upon running we can see the below output,

[root@vx111a inventory]# ansible-playbook command.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Check for Command ls] **************************************************
changed: [172.16.202.96]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=1    unreachable=0    failed=0  

Raw – This module is used when there is more need than Command module or if the command module does not support the operation. This module makes a SSH to the remote machine and run the command. For the Ansible to work we need to have Python available but for this module we don’t need a Python to be available. A simple playbook looks as,

[root@vx111a inventory]# cat raw.yml
---
- hosts: dev
  tasks:
   - name: Install VIM
     raw: yum -y install vim-common

[root@vx111a inventory]# ansible-playbook raw.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Install VIM] ***********************************************************
ok: [172.16.202.96]

PLAY RECAP ********************************************************************
172.16.202.96              : ok=2    changed=0    unreachable=0    failed=0  

Script – The third module is the Script module which helps in executing scripts on the more machine. This module will copy the script to the remote machine and will execute. A simple playbook looks as

Write a list_dir.sh script file with below contents as,
ls -l /etc | egrep '^d' | wc –l

Now write the playbook as,
[root@vx111a inventory]# cat script.yml
---
- hosts: dev
  tasks:
   - name: Count Number of Dir
     script: list_dir.sh /etc
     register: out

   - debug: var="{{ out.stdout }}" 

The output when we execute that will be,

[root@vx111a inventory]# ansible-playbook script.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [Count Number of Dir] ***************************************************
changed: [172.16.202.96]

TASK: [debug var="{{ out.stdout }}"] ******************************************
ok: [172.16.202.96] => {
    "var": {
        "153\n": "153\n"
    }
}

PLAY RECAP ********************************************************************
172.16.202.96              : ok=3    changed=1    unreachable=0    failed=0  

Shell – The Shell module also allows you to execute commands on the remote machine. The major difference between the Command and the Shell modules are that the Shell module uses a shell (/bin/bash) to run commands. We can get all the shell variables and use other shell features. A simple play book includes,

[root@vx111a inventory]# cat shell.yml
---
- hosts: dev
  tasks:
   - name: create File
     shell: /bin/touch /tmp/hai
     register: out

   - debug: var="{{ out.stdout }}"


[root@vx111a inventory]# ansible-playbook shell.yml

PLAY [dev] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [172.16.202.96]

TASK: [create File] ***********************************************************
changed: [172.16.202.96]

TASK: [debug var="{{ out.stdout }}"] ******************************************
ok: [172.16.202.96] => {
    "var": {
        "": ""
    }
}

PLAY RECAP ********************************************************************
172.16.202.96              : ok=3    changed=1    unreachable=0    failed=0  

File – There are certain modules available in Ansible that helps in managing files, directories etc. One such module is File. The file module allows you to change the attributes of a file. It can touch a file, create or delete recursive directories, and create or delete symlinks.

A simple playbook includes setting a file /tmp/hai with 644 permissions and creating a directory in tmr in /tmp

---
- hosts: dev
  tasks:
   - name: File Permissions
     file: dest=/tmp/hai mode=0644

   - name: Create Directory
     file: dest=/tmp/tmr state=directory

Copy – Another module in managing file is Copy module. A Copy module allows in copying file to the remote location. A Simple example yml file is

[root@vx111a inventory]# cat copy.yml
---
- hosts: dev
  tasks:
   - name: Copy File
     copy: src=./shell.yml dest=/tmp

Copies a file shell.yml to the /tmp location on remote machine.

Template – Template module is a very important module provided by Ansible. Templating is nothing but creating file on the machine. Ansible uses Jinja2 templating language for creating templates based on the Django Framework.

Create a test file as
[root@vx111a inventory]# cat test
This is a test file in {{ ansible_hostname }}

Now lets copy the test file to the remote system using the below playbook as,

[root@vx111a inventory]# cat template.yml
---
- hosts: dev
  tasks:
   - name: Create a Test Template
     template: src=test dest=/tmp/testFile mode=0644

Once we execute the playbook we can see that the file is not just moved but the variable “Ansible_hostname” is replaced with the Hostname variable.

Another option provided by template is the validate parameter that allows you to run a command to validate the file before copying it. This is like a hook that Ansible provides to make sure files that might break the service are not written. 

A simple playbook for copying a conf file to the HTTPD location and validating it before copying that look as,

[root@vx111a inventory]# cat template1.yml
---
- hosts: dev
  tasks:
   - name: Create a Virtaul Host and Validate
     template: src=test.conf dest=/etc/httpd/conf.d/test.conf validate="httpd -t %s"

Upon executing the Playbiook it throws an error as Validation failed.

That’s all for now. We will be seeing more modules along with examples in the next articles.
Read More