Skip to main content

Tasks & Modules

Task Structure

Basic Task Syntax

- name: Task description
module_name:
parameter1: value1
parameter2: value2
become: yes
when: condition
notify: handler_name
tags: tag_name
register: result_variable

Task with All Options

- name: Complete task example
package:
name: nginx
state: present
become: yes
become_user: root
become_method: sudo
delegate_to: localhost
delegate_facts: yes
run_once: true
environment:
PATH: /usr/local/bin:{{ ansible_env.PATH }}
async: 3600
poll: 0
retries: 3
delay: 10
timeout: 300
until: result.stdout.find("success") != -1
when: ansible_os_family == "Debian"
changed_when: false
failed_when: result.rc != 0
ignore_errors: yes
ignore_unreachable: yes
no_log: true
notify: restart nginx
tags: [installation, packages]
register: install_result

Core Modules

System Modules

package

- name: Install package (cross-platform)
package:
name: nginx
state: present

- name: Install multiple packages
package:
name:
- nginx
- mysql-server
- php
state: present

- name: Remove package
package:
name: apache2
state: absent

apt (Debian/Ubuntu)

- name: Update package cache
apt:
update_cache: yes
cache_valid_time: 3600

- name: Install package from specific repository
apt:
name: nginx
state: present
default_release: xenial-backports

- name: Install .deb package
apt:
deb: /path/to/package.deb
state: present

- name: Upgrade all packages
apt:
upgrade: dist
update_cache: yes

yum/dnf (RHEL/CentOS/Fedora)

- name: Install package with yum
yum:
name: nginx
state: present

- name: Install package with dnf
dnf:
name: nginx
state: present

- name: Install from URL
yum:
name: https://example.com/package.rpm
state: present

- name: Update all packages
yum:
name: '*'
state: latest

service

- name: Start and enable service
service:
name: nginx
state: started
enabled: yes

- name: Stop service
service:
name: nginx
state: stopped

- name: Restart service
service:
name: nginx
state: restarted

- name: Reload service
service:
name: nginx
state: reloaded

systemd

- name: Start systemd service
systemd:
name: nginx
state: started
enabled: yes
daemon_reload: yes

- name: Mask service
systemd:
name: nginx
masked: yes

- name: Check service status
systemd:
name: nginx
register: service_status

File System Modules

file

- name: Create directory
file:
path: /opt/app
state: directory
owner: app
group: app
mode: '0755'

- name: Create file
file:
path: /tmp/test.txt
state: touch
owner: root
group: root
mode: '0644'

- name: Create symlink
file:
src: /opt/app/current
dest: /opt/app/app-1.0
state: link

- name: Remove file
file:
path: /tmp/test.txt
state: absent

copy

- name: Copy file
copy:
src: /local/path/file.txt
dest: /remote/path/file.txt
owner: root
group: root
mode: '0644'
backup: yes

- name: Copy with content
copy:
content: |
This is the content
of the file
dest: /tmp/content.txt
owner: root
group: root
mode: '0644'

template

- name: Generate config file
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
notify: restart nginx

fetch

- name: Fetch file from remote
fetch:
src: /remote/path/file.txt
dest: /local/path/
flat: yes
fail_on_missing: no

synchronize

- name: Synchronize directories
synchronize:
src: /local/path/
dest: /remote/path/
delete: yes
recursive: yes
rsync_opts:
- '--exclude=*.log'
- '--exclude=.git'

User Management Modules

user

- name: Create user
user:
name: appuser
uid: 1001
group: app
groups: wheel,docker
home: /home/appuser
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
create_home: yes
append: yes

- name: Remove user
user:
name: olduser
state: absent
remove: yes

group

- name: Create group
group:
name: app
gid: 1001
state: present

- name: Remove group
group:
name: oldgroup
state: absent

authorized_key

- name: Add SSH key
authorized_key:
user: appuser
key: "{{ lookup('file', '/home/ansible/.ssh/id_rsa.pub') }}"
state: present

- name: Add multiple SSH keys
authorized_key:
user: appuser
key: '{{ item }}'
state: present
loop:
- 'ssh-rsa AAAAB3NzaC1yc2E... user1@example.com'
- 'ssh-rsa AAAAB3NzaC1yc2E... user2@example.com'

Network Modules

uri

- name: Test web service
uri:
url: http://example.com/api/health
method: GET
status_code: 200
register: health_check

- name: POST data to API
uri:
url: http://example.com/api/data
method: POST
body_format: json
body:
key: value
data: '{{ variable }}'
headers:
Content-Type: application/json
Authorization: 'Bearer {{ api_token }}'

get_url

- name: Download file
get_url:
url: https://example.com/file.tar.gz
dest: /tmp/file.tar.gz
mode: '0644'
timeout: 30
validate_certs: yes

firewalld

- name: Configure firewall
firewalld:
service: http
permanent: yes
state: enabled
immediate: yes

- name: Open port
firewalld:
port: 8080/tcp
permanent: yes
state: enabled
immediate: yes

Database Modules

mysql_user

- name: Create MySQL user
mysql_user:
name: appuser
password: '{{ mysql_password }}'
priv: 'appdb.*:ALL'
host: '%'
state: present
login_user: root
login_password: '{{ mysql_root_password }}'

mysql_db

- name: Create MySQL database
mysql_db:
name: appdb
state: present
encoding: utf8
collation: utf8_general_ci
login_user: root
login_password: '{{ mysql_root_password }}'

postgresql_user

- name: Create PostgreSQL user
postgresql_user:
name: appuser
password: '{{ postgres_password }}'
role_attr_flags: CREATEDB,NOSUPERUSER
state: present
become_user: postgres

postgresql_db

- name: Create PostgreSQL database
postgresql_db:
name: appdb
owner: appuser
state: present
encoding: UTF-8
template: template0
become_user: postgres

Cloud Modules

ec2

- name: Launch EC2 instance
ec2:
key_name: mykey
instance_type: t2.micro
image: ami-12345678
wait: yes
group: webserver
count: 1
vpc_subnet_id: subnet-12345678
assign_public_ip: yes
region: us-east-1
instance_tags:
Name: webserver
Environment: production
register: ec2_instance

azure_rm_virtualmachine

- name: Create Azure VM
azure_rm_virtualmachine:
resource_group: myResourceGroup
name: myVM
vm_size: Standard_B2s
admin_username: azureuser
ssh_password_enabled: false
ssh_public_keys:
- path: /home/azureuser/.ssh/authorized_keys
key_data: '{{ ssh_public_key }}'
image:
offer: UbuntuServer
publisher: Canonical
sku: 18.04-LTS
version: latest

Command and Shell Modules

command

- name: Run command
command: ls -la /tmp
register: ls_result

- name: Run command with arguments
command:
cmd: /usr/bin/command
chdir: /tmp
creates: /tmp/output.txt
register: command_result

shell

- name: Run shell command
shell: |
echo "Starting backup"
tar -czf /backup/$(date +%Y%m%d).tar.gz /data/
echo "Backup completed"
args:
executable: /bin/bash
chdir: /tmp
register: backup_result

script

- name: Run local script on remote host
script: /local/path/script.sh
args:
creates: /tmp/script.done
register: script_result

raw

- name: Run raw command (no Python required)
raw: cat /etc/os-release
register: os_info

Error Handling in Tasks

Conditional Execution

- name: Task with conditions
package:
name: nginx
state: present
when:
- ansible_os_family == "Debian"
- ansible_distribution_version is version('18.04', '>=')

Custom Error Conditions

- name: Command with custom failure
shell: /path/to/command
register: result
failed_when:
- result.rc != 0
- "'ERROR' in result.stdout"
changed_when: "'CHANGED' in result.stdout"

Retry Logic

- name: Task with retry
uri:
url: http://example.com/api
method: GET
register: api_result
retries: 5
delay: 10
until: api_result.status == 200

Advanced Task Features

Async Tasks

- name: Long running task
shell: /path/to/long-running-script.sh
async: 3600
poll: 0
register: long_task

- name: Check async task status
async_status:
jid: '{{ long_task.ansible_job_id }}'
register: job_result
until: job_result.finished
retries: 30
delay: 60

Delegation

- name: Run task on different host
command: echo "Running on database server"
delegate_to: db1.example.com
delegate_facts: yes

- name: Run task locally
command: echo "Running on control machine"
delegate_to: localhost

Environment Variables

- name: Task with environment variables
shell: echo $CUSTOM_VAR
environment:
CUSTOM_VAR: custom_value
PATH: /usr/local/bin:{{ ansible_env.PATH }}
HOME: /home/appuser

Module Documentation

Getting Module Help

# List all modules
ansible-doc -l

# Get module documentation
ansible-doc copy
ansible-doc -s copy # Short format

# Search for modules
ansible-doc -l | grep mysql

# Module examples
ansible-doc copy | grep -A 20 "EXAMPLES:"

Module Testing

- name: Test module functionality
debug:
msg: 'Testing module'
check_mode: yes

- name: Verify module results
assert:
that:
- result.changed == true
- result.failed == false
fail_msg: 'Module did not work as expected'

Custom Modules

Simple Custom Module

#!/usr/bin/python
# library/hello.py

from ansible.module_utils.basic import AnsibleModule

def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(type='str', required=True),
greeting=dict(type='str', default='Hello')
)
)

name = module.params['name']
greeting = module.params['greeting']

result = {
'changed': False,
'message': f'{greeting}, {name}!'
}

module.exit_json(**result)

if __name__ == '__main__':
main()

Using Custom Module

- name: Use custom module
hello:
name: World
greeting: Hi
register: hello_result

- name: Show custom module result
debug:
msg: '{{ hello_result.message }}'

Best Practices

Task Organization

- name: Well-organized tasks
block:
- name: Install packages
package:
name: '{{ item }}'
state: present
loop:
- nginx
- mysql-server
tags: installation

- name: Configure services
template:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
loop:
- src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
- src: mysql.cnf.j2
dest: /etc/mysql/mysql.conf.d/custom.cnf
notify: restart services
tags: configuration

rescue:
- name: Handle installation errors
debug:
msg: 'Installation failed, rolling back'

always:
- name: Cleanup
file:
path: /tmp/install.lock
state: absent

Performance Optimization

- name: Optimized tasks
package:
name: '{{ packages }}' # Install multiple packages at once
state: present
vars:
packages:
- nginx
- mysql-server
- php

- name: Use appropriate modules
file:
path: /tmp/test
state: touch
# Better than: shell: touch /tmp/test

- name: Gather facts selectively
setup:
filter: ansible_distribution*
when: ansible_facts is undefined

Security Best Practices

- name: Secure task execution
user:
name: admin
password: "{{ admin_password | password_hash('sha512') }}"
no_log: true # Hide sensitive output
become: yes
become_user: root

- name: Secure file operations
file:
path: /etc/ssl/private/server.key
owner: root
group: root
mode: '0600'

- name: Use vault for sensitive data
template:
src: database.conf.j2
dest: /etc/app/database.conf
owner: app
group: app
mode: '0600'
vars:
db_password: '{{ vault_db_password }}'