Install software
- name: Install ntpdate
become: true
ansible.builtin.package:
name: ntpdate
state: present
use: dnf # optional
Using dnf:
- name: Install EPEL repo
become: true
ansible.builtin.dnf:
name: epel-release
state: present
update_cache: true
Install latest github release
- name: Add User and Group
hosts: localhost
vars:
helm_wanted_version: '3.12.3'
helm_architecture: 'amd64'
helm_mirror: 'https://get.helm.sh'
helm_install_dir: '/usr/local/bin/'
helm_download_dir: "{{ x_ansible_download_dir | default(ansible_env.HOME + '/.ansible/tmp/downloads') }}"
helm_os: 'linux'
tasks:
- name: Check current installed version
ansible.builtin.command: >-
{{ helm_install_dir }}/helm version --client --template
{{ "'{{ if .Version }}{{ .Version }}{{ else }}{{ .Client.SemVer }}{{ end }}'" }}
register: helm_current_version
failed_when: false
changed_when: false
- name: Register current version
set_fact:
helm_current_version: "{{ helm_current_version.stdout }}"
# - name: debug helm_current_version
# debug:
# msg: "{{ helm_current_version }}"
- name: Set the version to be installed to the defined version
set_fact:
helm_install_version: "{{ helm_wanted_version }}"
when:
- helm_wanted_version is defined
- helm_wanted_version != helm_current_version
- name: Compare latest version with current installed version if no wanted version exists
when: helm_wanted_version is not defined
block:
- name: Check latest release
uri:
url: https://api.github.com/repos/helm/helm/tags
method: GET
return_content: yes
status_code: 200
body_format: json
register: result
- name: Register latest release
set_fact:
helm_latest_version: "{{ result.json[0].name | regex_replace('^v', '') }}"
# - name: debug helm_latest_version
# debug:
# msg: "{{ helm_latest_version }}"
- name:
set_fact:
helm_install_version: "{{ helm_latest_version }}"
when:
- helm_latest_version != helm_current_version
# - name: Debug decision
# debug:
# msg: "Will install Helm {{ helm_install_version }}"
# when: helm_install_version is defined
- name: Install helm
when: helm_install_version is defined
block:
- name: create download directory
ansible.builtin.file:
state: directory
mode: 'u=rwx,go=rx'
dest: '{{ helm_download_dir }}'
- name: Set filename variable
set_fact:
helm_redis_filename: 'helm-v{{ helm_install_version }}-{{ helm_os }}-{{ helm_architecture }}.tar.gz'
- name: download sha256sum
ansible.builtin.get_url:
url: '{{ helm_mirror }}/{{ helm_redis_filename }}.sha256'
dest: '{{ helm_download_dir }}/{{ helm_redis_filename }}.sha256'
force: false
use_proxy: true
validate_certs: true
mode: 'u=rw,go=r'
- name: read sha256sum
ansible.builtin.slurp:
src: '{{ helm_download_dir }}/{{ helm_redis_filename }}.sha256'
register: helm_sha256sum
- name: download Helm
ansible.builtin.get_url:
url: '{{ helm_mirror }}/{{ helm_redis_filename }}'
dest: '{{ helm_download_dir }}/{{ helm_redis_filename }}'
checksum: 'sha256:{{ helm_sha256sum.content | b64decode | trim }}'
force: false
use_proxy: true
validate_certs: true
mode: 'u=rw,go=r'
- name: remove existing installation
become: true
ansible.builtin.file:
path: '{{ helm_install_dir }}/helm'
state: absent
when:
- helm_current_version is defined
- helm_current_version | length > 0
- name: install unarchive module dependencies (apt, yum, dnf, zypper)
become: true
ansible.builtin.package:
name:
- tar
- unzip
- gzip
state: present
when: ansible_pkg_mgr in ('apt', 'yum', 'dnf', 'zypper')
- name: Extract Helm
become: true
ansible.builtin.unarchive:
src: '{{ helm_download_dir }}/{{ helm_redis_filename }}'
dest: '{{ helm_download_dir }}'
remote_src: true
extra_opts:
- '--strip-components=1'
creates: '{{ helm_download_dir }}/helm'
- name: Install Helm
become: true
ansible.builtin.copy:
src: '{{ helm_download_dir }}/helm'
dest: '{{ helm_install_dir }}'
remote_src: true
owner: root
group: root
mode: 'u=rwx,go=rx'
- name: Clean after installation
become: true
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- '{{ helm_download_dir }}/{{ helm_redis_filename }}'
- '{{ helm_download_dir }}/helm'
- '{{ helm_download_dir }}/LICENSE'
- '{{ helm_download_dir }}/README.md'
Files and folders
Create a directory
- name: Create dnf-automatic.timer.d directory for the override
become: true
ansible.builtin.file:
path: /etc/systemd/system/dnf-automatic.timer.d/
state: directory
owner: root
group: root
mode: 0755
Upload a file
- name: Upload sysctl config
become: true
ansible.builtin.copy:
src: 99-sysctl.conf
dest: "/etc/sysctl.d/"
owner: root
group: root
mode: 0600
Upload a folder
- name: Upload auditd files
become: true
ansible.builtin.copy:
src: "./files/auditd/rules.d"
dest: "/etc/audit/"
owner: root
group: root
mode: 0700
notify: Reload Auditd Rules
Create a file
- name: Set the time to trigger updates
become: true
ansible.builtin.copy:
content: |
[Timer]
OnCalendar=*-*-* 01:30
dest: '/etc/systemd/system/dnf-automatic.timer.d/override.conf'
owner: root
group: root
mode: 0644
notify:
- Reload systemd ## Call a handler
Create folder/directory
- name: Create directory
ansible.builtin.file:
state: directory
dest: '/path/to/directory'
mode: 'u=rwx,go=rx'
Delete a folder
Delete a file
Include tasks
- name: Include do-something task
ansible.builtin.include_tasks:
file: do-something.yml
apply:
become: true
become_user: "RunAsThisUserName"
Handlers
Flush Handlers
handlers run after all the tasks in a particular play have been completed. You can force them to run immediately with:
Templates
- name: Update sshd configuration safely, avoid locking yourself out
ansible.builtin.template:
src: etc/ssh/sshd_config.j2
dest: /etc/ssh/sshd_config
owner: root
group: root
mode: 0600
validate: /usr/sbin/sshd -t -f %s
backup: yes
Conditionals
With a variable called rol and a possible value of server or client, you can do the following. Source
{# style 1 - long form #}
{% if filepath == '/var/opt/tomcat_1' %}
{% set tomcat_value = tomcat_1_value %}
{% else %}
{% set tomcat_value = tomcat_2_value %}
{% endif %}
{# style 2 - short form #}
{% set tomcat_value = tomcat_1_value if (filepath == '/var/opt/tomcat_1') else tomcat_2_value %}
{# style 3 - with ternary filter #}
{% set tomcat_value = (filepath == '/var/opt/tomcat_1')|ternary(tomcat_1_value, tomcat_2_value) %}
<Server port={{ tomcat_value }} shutdown="SHUTDOWN">
systemd
Configure services
Restart service
- name: Restart service crond
become: true
ansible.builtin.systemd_service:
state: restarted
daemon_reload: true
name: crond
- name: Enable service crond
become: true
ansible.builtin.systemd_service:
enabled: true
daemon_reload: true
name: crond
daemon reload
- name: Reload systemd
become: true
ansible.builtin.systemd_service:
daemon_reload: true
- name: Reload systemd for your user
ansible.builtin.systemd_service:
daemon_reload: yes
scope: user
- name: Reload systemd for another user
become: true
become_user: "username"
ansible.builtin.systemd_service:
daemon_reload: true
scope: "user"
loops
loop
Loops are prefered over with_<lookup>.
- name: Add several users
ansible.builtin.user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
loop_control:
pause: 3
- name: Add several users
ansible.builtin.user:
name: "{{ users }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
loop_control:
loop_var: users
pause: 3
with_
Multiple examples in the Ansible loops documentation
Can be used with the following lookups
Example to create users ^2
- name: Creating users with_items
hosts: localhost
tasks:
- name: Create user
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
with_items:
- { name: joe, uid: 1010 }
- { name: george, uid: 1011 }
- { name: ravi, uid: 1012 }
loop over dictionaries
Using the lookup filter ^1
- name: Validate Services
hosts: myhosts
vars:
services:
nginx:
state: started
enabled: yes
httpd:
state: stopped
enabled: no
mysqld:
state: started
tasks:
- name: Ensure services are correct
service:
name: "{{ item.key }}"
state: "{{ item.value.state }}"
enabled: "{{ item.value.enabled | default('yes') }}"
loop: "{{ lookup('dict', services) }}"
Using the dict2items filter ^1
- name: Create user accounts from a dictionary
hosts: myhosts
vars:
users_dict:
mjordan:
uid: 1001
groups: "dev"
mmathers:
uid: 1002
groups: "prod"
tasks:
- name: Create user accounts
user:
name: "{{ item.key }}"
uid: "{{ item.value.uid }}"
groups: "{{ item.value.groups }}"
loop: "{{ users_dict | dict2items }}"
Control
Error handling
Error handling in Ansible's Doc
Ignore errors
Blocks
- name: Install, configure, and start Apache
when: ansible_facts['distribution'] == 'CentOS'
block:
- name: Install httpd and memcached
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
- name: Apply the foo config template
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
- name: Start service bar and enable it
ansible.builtin.service:
name: bar
state: started
enabled: True
become: true
become_user: root
ignore_errors: true
Containers
Podman
vars/main.yml
unpriv_user_name: noone
podman_data_path: "/path/to/containers/data"
podman_quadlet_path: "{{ unpriv_user_home }}/.config/containers/systemd"
podman_environment_path: "{{ unpriv_user_home }}/.config/containers/environment"
podman_cont_path: "{{ podman_data_path }}/cont"
podman_cont_path_config: "{{ podman_cont_path }}/config"
podman_cont_path_data: "{{ podman_cont_path }}/data"
podman_cont_path_quadlet: "{{ podman_quadlet_path }}/cont"
handlers/main.yml
- name: Reload Systemd User
become: true
become_user: "{{ unpriv_user_name }}"
ansible.builtin.systemd:
daemon_reload: true
scope: user
- name: Restart Containers
become: true
become_user: "{{ unpriv_user_name }}"
ansible.builtin.systemd_service:
state: restarted
daemon_reload: true
scope: user
name: "{{ item }}"
loop:
- "cont"
- name: Restart Cont
become: true
become_user: "{{ unpriv_user_name }}"
ansible.builtin.systemd_service:
state: restarted
daemon_reload: true
scope: user
name: cont
Playbook
- name: Create Cont Paths
ansible.builtin.file:
path: "{{ container_paths }}"
state: directory
owner: "{{ unpriv_user_name }}"
group: "{{ unpriv_user_name }}"
mode: 0770
loop:
- "{{ podman_cont_path }}" ## The main path
- "{{ podman_cont_path_config }}" ## Path to store the configuration
- "{{ podman_cont_path_quadlet }}" ## Path to store the quadlets
loop_control:
loop_var: container_paths
- name: Update Cont Configuration Files
ansible.builtin.copy:
src: "{{ configuration_file_src }}"
dest: "{{ podman_cont_path_config }}/"
owner: "{{ unpriv_user_name }}"
group: "{{ unpriv_user_name }}"
mode: 0770
loop:
- "./files/podman/cont/file"
loop_control:
loop_var: configuration_file_src
notify:
- Restart Cont
- name: Upload Cont Quadlets
ansible.builtin.copy:
src: "{{ quadlet_file_src }}"
dest: "{{ podman_quadlet_path }}/"
owner: "{{ unpriv_user_name }}"
group: "{{ unpriv_user_name }}"
mode: 0400
loop:
- "./files/podman/cont/cont.network"
- "./files/podman/cont/cont.volume"
- "./files/podman/cont/cont-certs.volume"
# - "./files/podman/cont/cont.container"
loop_control:
loop_var: quadlet_file_src
notify:
- Reload Systemd User
- Restart Cont
- name: Upload Cont Quadlets Template
ansible.builtin.template:
src: "./templates/podman/cont/cont.container.j2"
dest: "{{ podman_cont_path_quadlet }}/cont.container"
owner: "{{ unpriv_user_name }}"
group: "{{ unpriv_user_name }}"
mode: 0400
force: yes
notify:
- Reload Systemd User
- Restart Cont
templates/cont.container.j2
files/cont/cont.volume
files/cont/cont.network
[Unit]
Description=Cont Containers Network
[Network]
Label=app=cont
NetworkName=cont
[Install]
WantedBy=multi-user.target default.target