If you are using an older version of Linux based on Mint 18 or Ubuntu 16, you may want to get the PPA and get the latest version of Ansible that way:
sudo add-apt-repository ppa:ansible/ansible
sudo apt update
Some bugs in older Ansible versions are where unarchive will retrieve the remote_src as local and in mysql_user where it cannot assign the privileges needed to a user (it can't parse some of the GRANT queries). For the lineinline module it may say there is no parameter "path" found.
Requirements: A Linux machine (eg. VM whether in the Cloud or a local VM on Vbox/VMWare/Proxmox) that you can easily install Anisble on (eg. Debian/Ubuntu/Mint). The VM requires proper/working internet between the Ansible Controller and to the internet.
This will be on our "controller" / source machine which is where we deploy the Ansible Playbooks (.yaml) files from.
Install Ansible
sudo apt install ansible
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
ieee-data python-jinja2 python-netaddr python-yaml
Suggested packages:
python-jinja2-doc ipython python-netaddr-docs
Recommended packages:
python-selinux
The following NEW packages will be installed:
ansible ieee-data python-jinja2 python-netaddr python-yaml
0 upgraded, 5 newly installed, 0 to remove and 153 not upgraded.
Need to get 2,463 kB of archives.
After this operation, 15.7 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 python-jinja2 all 2.8-1ubuntu0.1 [106 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial/main amd64 python-yaml amd64 3.11-3build1 [105 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial/main amd64 ieee-data all 20150531.1 [830 kB]
Get:4 http://archive.ubuntu.com/ubuntu xenial/main amd64 python-netaddr all 0.7.18-1 [174 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial-backports/universe amd64 ansible all 2.1.1.0-1~ubuntu16.04.1 [1,249 kB]
Fetched 2,463 kB in 1s (1,474 kB/s)
Selecting previously unselected package python-jinja2.
(Reading database ... 434465 files and directories currently installed.)
Preparing to unpack .../python-jinja2_2.8-1ubuntu0.1_all.deb ...
Unpacking python-jinja2 (2.8-1ubuntu0.1) ...
Selecting previously unselected package python-yaml.
Preparing to unpack .../python-yaml_3.11-3build1_amd64.deb ...
Unpacking python-yaml (3.11-3build1) ...
Selecting previously unselected package ieee-data.
Preparing to unpack .../ieee-data_20150531.1_all.deb ...
Unpacking ieee-data (20150531.1) ...
Selecting previously unselected package python-netaddr.
Preparing to unpack .../python-netaddr_0.7.18-1_all.deb ...
Unpacking python-netaddr (0.7.18-1) ...
Selecting previously unselected package ansible.
Preparing to unpack .../ansible_2.1.1.0-1~ubuntu16.04.1_all.deb ...
Unpacking ansible (2.1.1.0-1~ubuntu16.04.1) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up python-jinja2 (2.8-1ubuntu0.1) ...
Setting up python-yaml (3.11-3build1) ...
Setting up ieee-data (20150531.1) ...
Setting up python-netaddr (0.7.18-1) ...
Setting up ansible (2.1.1.0-1~ubuntu16.04.1) ...
vi /etc/ansible/hosts
Let's make a new section/group called "lamp"
Change the IP 10.0.2.16 to the IP of your destination Linux VM
[lamp]
host1 ansible_ssh_host=10.0.2.16 #you could add host2,host3 and as many extra hosts as you want
sudo mkdir -p /etc/ansible/group_vars
vi /etc/ansible/group_vars/lamp
#note that the file name is lamp, if the group was called "abcgroup" then the filename would be "abcgroup" instead otherwise it has no impact if the filename does not match the group name.
ansible_ssh_user: root
#note that we can put other variables in this same file by adding more lines like above
#you could create another variable like this:
rtt_random_var: woot!
ansible -m ping all
#
We also could have specified ansible -m ping lamp to just check connectivity to the lamp group
Oops it didn't work!? But I can ping and ssh to it manually
host1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh.",
"unreachable": true
}
You could also use ssh-copy-id
to setup passwordless auth by key.
*I strongly recommend not using become or using any method that uses a manual password or password saved in a variable for both security reasons and convenience.
I especially don't recommend using -K or --ask-become-pass because it uses the same password for all hosts (all hosts should not have the same password). it is also inefficient and insecure to rely on typing the password each time when prompted and defeats the purposes of automation with Ansible.
Try again now that you have your key auth working (if it works you should be able to ssh as root to the server without any password)
host1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible -m shell -a 'free -m' host1
host1 | SUCCESS | rc=0 >>
total used free shared buff/cache available
Mem: 3946 84 3779 5 82 3700
Swap: 974 0 974
*Note we could swap host1 for "all" to do all servers or specify "lamp" for just the lamp group to execute that command on.
The sports analogy or possibly theatre inspired terms are really just slang for "it's a YAML" config file that Ansible then translates into specific commands and operations to achieve the automated task on the destination hosts.
Essentially the YAML you create is the equivalent of a script, think of YAML as a high-end language that is then translated into more complex, high-end commands to the destination server.
The difference between the Play and Playbook, is that a Play is more like a single chapter book (a single play, possibly something like just starting Apache). A Playbook is made up of "multiple Plays" or chapters, that essentially execute a number of ordered plays, usually to achieve a larger and more complex task (eg. install LAMP, then create a DB for Wordpress, then install and configure Wordpress etc.. would be done as a Playbook normally).
1.) It has a list of hosts (eg. a group like lamp that we created earlier).
2.) A list of task(s) to execute on the remote host(s)
*Note that it is indenation sensitive and spacing sensitive, as in the real syntax is based on spacing and the dashes -
---
- hosts: lamp
become: yes
tasks:
- name: install apache2
apt: name=apache2 update_cache=yes state=latest
ansible-playbook areebapache.yaml
_____________
< PLAY [lamp] >
-------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
______________
< TASK [setup] >
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [host1]
________________________
< TASK [install apache2] >
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [host1]
____________
< PLAY RECAP >
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
host1 : ok=2 changed=1 unreachable=0 failed=0
You should be able to visit the IP of each host in the lamp group and see the default Apache2 Debian index
Which playbook works and why, what is different about the two? (Feel free to run each one).
#book 1
---
- hosts: lamp
become: root
tasks:
- name: Install apache2
apt: name=apache2 state=latest
#book 2
---
- hosts: lamp
become: root
tasks:
- name: Install apache2
apt: name=apache2 state=latest
Facts are like default, builtin environment variables that we can use to access information about the target:
Get facts by using "ansible NAME -m setup
"
You can replace NAME with a specific host, all or a group name.
For example if we wanted the ipv4 address we would use this notation to get the nested "address" we added a dot after ansible_default_ipv4
"ansible_default_ipv4": {
"address": "10.0.2.16",
{{ansible_default_ipv4.address}}
host1 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"10.0.2.16"
],
"ansible_all_ipv6_addresses": [
"fec0::dcad:beff:feef:682",
"fe80::dcad:beff:feef:682"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "04/01/2014",
"ansible_bios_version": "Ubuntu-1.8.2-1ubuntu1",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-4.19.0-18-amd64",
"quiet": true,
"ro": true,
"root": "UUID=78481d95-1470-42f0-bf4f-2dd841e4412a"
},
"ansible_date_time": {
"date": "2022-01-25",
"day": "25",
"epoch": "1643136984",
"hour": "13",
"iso8601": "2022-01-25T18:56:24Z",
"iso8601_basic": "20220125T135624284357",
"iso8601_basic_short": "20220125T135624",
"iso8601_micro": "2022-01-25T18:56:24.284585Z",
"minute": "56",
"month": "01",
"second": "24",
"time": "13:56:24",
"tz": "EST",
"tz_offset": "-0500",
"weekday": "Tuesday",
"weekday_number": "2",
"weeknumber": "04",
"year": "2022"
},
"ansible_default_ipv4": {
"address": "10.0.2.16",
"alias": "ens3",
"broadcast": "10.0.2.255",
"gateway": "10.0.2.2",
"interface": "ens3",
"macaddress": "de:ad:be:ef:06:82",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.0.2.0",
"type": "ether"
},
"ansible_default_ipv6": {
"address": "fec0::dcad:beff:feef:682",
"gateway": "fe80::2",
"interface": "ens3",
"macaddress": "de:ad:be:ef:06:82",
"mtu": 1500,
"prefix": "64",
"scope": "site",
"type": "ether"
},
"ansible_devices": {
"fd0": {
"holders": [],
"host": "",
"model": null,
"partitions": {},
"removable": "1",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "cfq",
"sectors": "8",
"sectorsize": "512",
"size": "4.00 KB",
"support_discard": "0",
"vendor": null
},
"sr0": {
"holders": [],
"host": "IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]",
"model": "QEMU DVD-ROM",
"partitions": {},
"removable": "1",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "688128",
"sectorsize": "2048",
"size": "1.31 GB",
"support_discard": "0",
"vendor": "QEMU"
},
"vda": {
"holders": [],
"host": "SCSI storage controller: Red Hat, Inc Virtio block device",
"model": null,
"partitions": {
"vda1": {
"sectors": "18968576",
"sectorsize": 512,
"size": "9.04 GB",
"start": "2048"
},
"vda2": {
"sectors": "2",
"sectorsize": 512,
"size": "1.00 KB",
"start": "18972670"
},
"vda5": {
"sectors": "1996800",
"sectorsize": 512,
"size": "975.00 MB",
"start": "18972672"
}
},
"removable": "0",
"rotational": "1",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "mq-deadline",
"sectors": "20971520",
"sectorsize": "512",
"size": "10.00 GB",
"support_discard": "0",
"vendor": "0x1af4"
}
},
"ansible_distribution": "Debian",
"ansible_distribution_major_version": "10",
"ansible_distribution_release": "buster",
"ansible_distribution_version": "10.11",
"ansible_dns": {
"nameservers": [
"10.0.2.3"
]
},
"ansible_domain": "ca",
"ansible_ens3": {
"active": true,
"device": "ens3",
"ipv4": {
"address": "10.0.2.16",
"broadcast": "10.0.2.255",
"netmask": "255.255.255.0",
"network": "10.0.2.0"
},
"ipv6": [
{
"address": "fec0::dcad:beff:feef:682",
"prefix": "64",
"scope": "site"
},
{
"address": "fe80::dcad:beff:feef:682",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "de:ad:be:ef:06:82",
"module": "virtio_net",
"mtu": 1500,
"pciid": "virtio0",
"promisc": false,
"type": "ether"
},
"ansible_env": {
"HOME": "/root",
"LANG": "C",
"LC_ALL": "C",
"LC_MESSAGES": "C",
"LOGNAME": "root",
"MAIL": "/var/mail/root",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PWD": "/root",
"SHELL": "/bin/bash",
"SHLVL": "0",
"SSH_CLIENT": "10.0.2.15 34260 22",
"SSH_CONNECTION": "10.0.2.15 34260 10.0.2.16 22",
"SSH_TTY": "/dev/pts/0",
"TERM": "xterm",
"USER": "root",
"XDG_RUNTIME_DIR": "/run/user/0",
"XDG_SESSION_CLASS": "user",
"XDG_SESSION_ID": "79",
"XDG_SESSION_TYPE": "tty",
"_": "/bin/sh"
},
"ansible_fips": false,
"ansible_form_factor": "Other",
"ansible_fqdn": "areeb-ansible.ca",
"ansible_gather_subset": [
"hardware",
"network",
"virtual"
],
"ansible_hostname": "areeb-ansible",
"ansible_interfaces": [
"lo",
"ens3"
],
"ansible_kernel": "4.19.0-18-amd64",
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"broadcast": "host",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"type": "loopback"
},
"ansible_lsb": {
"codename": "buster",
"description": "Debian GNU/Linux 10 (buster)",
"id": "Debian",
"major_release": "10",
"release": "10"
},
"ansible_machine": "x86_64",
"ansible_machine_id": "3c9b9946e31e46d39d7fc12c28fcf2c7",
"ansible_memfree_mb": 3567,
"ansible_memory_mb": {
"nocache": {
"free": 3738,
"used": 208
},
"real": {
"free": 3567,
"total": 3946,
"used": 379
},
"swap": {
"cached": 0,
"free": 974,
"total": 974,
"used": 0
}
},
"ansible_memtotal_mb": 3946,
"ansible_mounts": [
{
"device": "/dev/vda1",
"fstype": "ext4",
"mount": "/",
"options": "rw,relatime,errors=remount-ro",
"size_available": 7193808896,
"size_total": 9492197376,
"uuid": "78481d95-1470-42f0-bf4f-2dd841e4412a"
}
],
"ansible_nodename": "areeb-ansible",
"ansible_os_family": "Debian",
"ansible_pkg_mgr": "apt",
"ansible_processor": [
"GenuineIntel",
"KVM @ 2.00GHz",
"GenuineIntel",
"KVM @ 2.00GHz",
"GenuineIntel",
"KVM @ 2.00GHz",
"GenuineIntel",
"KVM @ 2.00GHz",
"GenuineIntel",
"KVM @ 2.00GHz",
"GenuineIntel",
"KVM @ 2.00GHz"
],
"ansible_processor_cores": 1,
"ansible_processor_count": 6,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 6,
"ansible_product_name": "Standard PC (i440FX + PIIX, 1996)",
"ansible_product_serial": "NA",
"ansible_product_uuid": "NA",
"ansible_product_version": "pc-i440fx-xenial",
"ansible_python": {
"executable": "/usr/bin/python",
"has_sslcontext": true,
"type": "CPython",
"version": {
"major": 2,
"micro": 16,
"minor": 7,
"releaselevel": "final",
"serial": 0
},
"version_info": [
2,
7,
16,
"final",
0
]
},
"ansible_python_version": "2.7.16",
"ansible_selinux": false,
"ansible_service_mgr": "systemd",
"ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAeJa04CWRa6N2zV+hKt+utDxOVI/23Zntb815bXz+qqK/XZsFoIEL7jYUZFlifJFAxmWgE9CJ6Vtn/4DzHnDx4=",
"ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIBhlJVY9PgACISzzqwviVOgeosQBWAKULGY4UsSRzbKJ",
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQC3rT0zeZS8TS7+XmYIt2aTAK1L/RHAhbJ54+UqpyXRJ0CmlQZySdh6ug65lK6VYMQMrmxC8niKVQ/1pSia2swJjb/qSyRlEUGnGYR8xGmVG1I99OcH1301E3nzvmJw44bcRKx/zf5CYf16X8KAPoNg9EsagvjGB5CYz3b5/x4fJmwJ2Qp7rPgNvDYp2GIqRcCXvtfui1vhf2eSqzDLFeK0nFfGqMj8mrBZn2UPRtJNKd3aFWyTqEePKT3Mm1B1cBgdh3St76X7kw0dKuY1BUqtZAOOGEUw84c/vLAeRmQx5yh78COf6ys5jltj6MBwCZ2iSTLAapRxxh13LQ7oAgIh",
"ansible_swapfree_mb": 974,
"ansible_swaptotal_mb": 974,
"ansible_system": "Linux",
"ansible_system_capabilities": [
"cap_chown",
"cap_dac_override",
"cap_dac_read_search",
"cap_fowner",
"cap_fsetid",
"cap_kill",
"cap_setgid",
"cap_setuid",
"cap_setpcap",
"cap_linux_immutable",
"cap_net_bind_service",
"cap_net_broadcast",
"cap_net_admin",
"cap_net_raw",
"cap_ipc_lock",
"cap_ipc_owner",
"cap_sys_module",
"cap_sys_rawio",
"cap_sys_chroot",
"cap_sys_ptrace",
"cap_sys_pacct",
"cap_sys_admin",
"cap_sys_boot",
"cap_sys_nice",
"cap_sys_resource",
"cap_sys_time",
"cap_sys_tty_config",
"cap_mknod",
"cap_lease",
"cap_audit_write",
"cap_audit_control",
"cap_setfcap",
"cap_mac_override",
"cap_mac_admin",
"cap_syslog",
"cap_wake_alarm",
"cap_block_suspend",
"cap_audit_read+ep"
],
"ansible_system_capabilities_enforced": "True",
"ansible_system_vendor": "QEMU",
"ansible_uptime_seconds": 77628,
"ansible_user_dir": "/root",
"ansible_user_gecos": "root",
"ansible_user_gid": 0,
"ansible_user_id": "root",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 0,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "kvm",
"module_setup": true
},
"changed": false
}
---
- hosts: lamp
become: root
#note we can put variables here under vars: and we can override variables from group_vars or elsewhere by redefining existing variables with new values (eg. ansible_ssh_user: somefakeuser). You can also even use variable placeholders within the .yml file later on eg. to specify a file path like src: "/some/path/{{thevarname}}"
vars:
avarhere: hellothere
tasks:
- name: Install apache2
apt: name=apache2 state=latest
- name: Install MySQL (really MariaDB now)
apt: name=mariadb-server state=latest
- name: Install php
apt: name=php state=latest
- name: Install php-cgi
apt: name=php-cgi state=latest
- name: Install php-cli
apt: name=php-cli state=latest
- name: Install apache2 php module
apt: name=libapache2-mod-php state=latest
- name: Install php-mysql
apt: name=php-mysql state=latest
Simply add on more tasks to your existing playbook above.
Wordpress requires a database like MariaDB and PHP (installed in our original playbook).
But what else is needed?
#MySQL config
- name: Create MySQL Database
mysql_db:
name: "{{wpdbname}}"
# ignore_errors: yes
- name: Create DB user/pass and give the user all privileges
mysql_user:
name: "{{wpdbuser}}"
password: "{{wpdbpass}}"
priv: '{{wpdbname}}.*:ALL'
state: present
# ignore_errors: yes
#Wordpress stuff
- name: Download and tar -zxvf wordpress
unarchive:
src: https://wordpress.org/latest.tar.gz
remote_src: yes
dest: "{{ wppath }}"
extra_opts: [--strip-components=1]
#creates: "{{ wppath }}"
- name: Set permissions
file:
path: "{{wppath}}"
state: directory
recurse: yes
owner: www-data
group: www-data
- name: copy the config file wp-config-sample.php to wp-config.php so we can edit it
command: mv {{wppath}}/wp-config-sample.php {{wppath}}/wp-config.php #creates={{wppath}}/wp-config.php
become: yes
- name: Update WordPress config file
lineinfile:
path: "{{wppath}}/wp-config.php"
regexp: "{{item.regexp}}"
line: "{{item.line}}"
with_items:
- {'regexp': "define\\( 'DB_NAME', '(.)+' \\);", 'line': "define( 'DB_NAME', '{{wpdbname}}' );"}
- {'regexp': "define\\( 'DB_USER', '(.)+' \\);", 'line': "define( 'DB_USER', '{{wpdbuser}}' );"}
- {'regexp': "define\\( 'DB_PASSWORD', '(.)+' \\);", 'line': "define( 'DB_PASSWORD', '{{wpdbpass}}' );"}
---
- hosts: all
become: root
# we can put variables here too that work in addition to what is in group_vars
ignore_errors: yes
vars:
auser: hellothere
ansible_ssh_user: root
wpdbname: rttdbname
wpdbuser: rttdbuser
wpdbpass: rttinsecurepass
wpdbhost: localhost
wppath: "/var/www/html"
tasks:
- name: Install apache2
apt: name=apache2 state=latest
notify:
- restart apache2
- name: Install MySQL (really MariaDB now)
apt: name=mariadb-server state=latest
- name: Install MySQL python module
apt: name=python-mysqldb state=latest
- name: Install php
apt: name=php state=latest
- name: Install apache2 php module
apt: name=libapache2-mod-php state=latest
- name: Install php-mysql
apt: name=php-mysql state=latest
#MySQL config
- name: Create MySQL Database
mysql_db:
name: "{{wpdbname}}"
# ignore_errors: yes
- name: Create DB user/pass and give the user all privileges
mysql_user:
name: "{{wpdbuser}}"
password: "{{wpdbpass}}"
priv: '{{wpdbname}}.*:ALL'
state: present
# ignore_errors: yes
- name: Copy index test page
template:
src: "files/index.html.j2"
dest: "/var/www/html/index.html"
- name: enable Apache2 service
service: name=apache2 enabled=yes
#Wordpress stuff
- name: Download and tar -zxvf wordpress
unarchive:
src: https://wordpress.org/latest.tar.gz
remote_src: yes
dest: "{{ wppath }}"
extra_opts: [--strip-components=1]
#creates: "{{ wppath }}"
- name: Set permissions
file:
path: "{{wppath}}"
state: directory
recurse: yes
owner: www-data
group: www-data
- name: copy the config file wp-config-sample.php to wp-config.php so we can edit it
command: mv {{wppath}}/wp-config-sample.php {{wppath}}/wp-config.php #creates={{wppath}}/wp-config.php
become: yes
- name: Update WordPress config file
lineinfile:
path: "{{wppath}}/wp-config.php"
regexp: "{{item.regexp}}"
line: "{{item.line}}"
with_items:
- {'regexp': "define\\( 'DB_NAME', '(.)+' \\);", 'line': "define( 'DB_NAME', '{{wpdbname}}' );"}
- {'regexp': "define\\( 'DB_USER', '(.)+' \\);", 'line': "define( 'DB_USER', '{{wpdbuser}}' );"}
- {'regexp': "define\\( 'DB_PASSWORD', '(.)+' \\);", 'line': "define( 'DB_PASSWORD', '{{wpdbpass}}' );"}
- name: restart apache2
service: name=apache2 state=restarted
We can use conditionals (eg like an if statement equivalent) to change the behavior. For example the playbook above installs python-mysqldb on the target, however it works on Debian 10 but not Debian 11 (since that package is deprecrated so we need to install python3-mysqldb instead). How can we do it?
#install python-mysqldb only if we are Debian 10
- name: Install MySQL python2 module Debian 10
apt: name=python-mysqldb state=latest
when: (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "10")
- name: Install MySQL python3 module Debian 11
apt: name=python3-mysqldb state=latest
when: (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "11")
Seeing it in action, you will see that only one of the two tasks is executed which is the Debian 11 task since the conditional of when matched Debian 11.
More on Ansible conditionals from the documentation.
It would also be wise under the apt: module to add "update_cache=yes" to make sure the packages are up to date.
We could put all of the apt install tasks from the original example into a single task like this:
---
- hosts: lamp
become: yes
tasks:
- name: install LAMP
apt: name={{item}} update_cache=yes state=latest
with_items:
- apache2
- mariadb-server
- php
- php-cgi
- php-cli
- libapache2-mod-php
- php-mysql
#note that the below won't work on older Ansible (eg. 2.1 and will throw a formatting error). If that happens, use the above playbook. I find the style above to be less prone to typos.
ERROR! The field 'loop' is supposed to be a string type, however the incoming data structure is a
The error appears to have been in '/home/markmenow/Ansible/lamp-fullloop.yaml': line 5, column 9, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- name: install LAMP
^ here
---
- hosts: lamp
become: yes
tasks:
- name: install LAMP
apt: name={{item}} state=latest
loop: [ 'apache2', 'mariadb-server', 'php', 'php-cgi', 'php-cli', 'libapache2-mod-php', 'php-mysql' ]
The only downside is that it can be harder to troubleshoot if something fails, since we are installing all of the items as a single apt command in a single task.
By default, Ansible will stop executing the playbook and not move on to the next task. For some reasons there are times where this is no the desirable or correct behavior.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html
You can tell an individual task to ignore errors and continue:
We just add ignore_errors at the same indentation level as our module.
- name: Create DB user/pass and give the user all privileges
mysql_user:
name: "{{wpdbuser}}"
password: "{{wpdbpass}}"
priv: '{{wpdbname}}.*:ALL'
state: present
ignore_errors: yes
We could also do a universal ignore_errors: yes which would apply to all tasks, but this is normally not what you'd want.
---
- hosts: lamp
become: yes
ignore_errors: yes
Handlers - Add this to the end of the above playbook.
handlers:
- name: restart apache2
service: name=apache2 state=restarted
More on handlers from Ansible: https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html
How do we enable a service so it works upon boot?
- name: enable Apache2 service
service: name=apache2 enabled=yes
How can we copy a file?
- name: Copy some file
copy:
src: "files/somefile.ext"
dest: "/var/some/dest/path/"
How can we tell Apache to use a custom index.html?
template means it is a jinja2 file which causes Ansible to replace variables based on the placeholder specified with double braces (eg {{varname}} ). If the varname is not found Ansible will throw an error and not replace the undefined variable and cause the playbook to fail (from the point that the template is called):
fatal: [host1]: FAILED! => {"changed": false, "failed": true, "msg": "AnsibleUndefinedVariable: 'auserr' is undefined"}
- name: Copy index test page
template:
src: "files/index.html.j2"
dest: "/var/www/html/index.html"
To make this work you would need to define the variables in the index.html above within your group_vars file or within the .yml playbook file.
How can we enable an Apache module?
-name: Apache Module - mod_rewrite
apache2_module:
state: present
name: rewrite
How can we enable htaccess?
Inside your files directory (based on the relative dir) place these contents into a file called "htaccess.conf"
*Note you would change the /var/www to another path such as /www/vhosts/ if your vhost directory was different than Apache's default /var/www
Create a new task to actually copy the htaccess enable file into Apache2's config directory on the target server:
- name: Enable htaccess support in /var/www
template:
src: "files/htaccess.conf"
dest: "/etc/apache2/sites-available/htaccess.conf"
Don't forget to symlink it to sites-enabled (which actually enables the htaccess
- name: Enable the htaccess.conf by copying to sites-enabled
file:
src: /etc/apache2/sites-available/htaccess.conf
dest: /etc/apache2/sites-enabled/htaccess.conf
state: link
Edit /etc/ansible/ansible.cfg
Set this line: cow_selection = random
https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html
https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
https://docs.ansible.com/ansible/2.3/playbooks_variables.html
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html
https://github.com/ansible/ansible-examples
https://docs.ansible.com/ansible-core/devel/reference_appendices/YAMLSyntax.html
https://docs.ansible.com/ansible-core/devel/reference_appendices/playbooks_keywords.html
ansible, tutorial, playbook, install, deploy, wordpress, server, requirements, linux, eg, vm, vbox, vmware, proxmox, anisble, debian, ubuntu, mint, requires, controller, quot, playbooks, yaml, sudo, apt, lists, dependency, additional, packages, installed, ieee, python, jinja, netaddr, ipython, docs, recommended, selinux, upgraded, newly, kb, archives, mb, disk, http, archive, xenial, updates, amd, backports, fetched, selecting, previously, unselected, database, directories, currently, preparing, unpack, _, _all, deb, unpacking, yaml_, _amd, data_, netaddr_, ansible_, processing, triggers, db, hosts, vi, etc, ip, destination, ansible_ssh_host, ssh, username, mkdir, group_vars, abcgroup, filename, ansible_ssh_user, ping, specified, connectivity, didn, manually, unreachable, msg, via, automated, prompt, password, ll, authentication, auth, pong, uptime, shell, commands, rc, buff, cache, mem, swap, analogy, theatre, inspired, slang, config, translates, operations, achieve, task, essentially, equivalent, translated, apache, multiple, chapters, execute, larger, configure, valid, indenation, spacing, syntax, dashes, tasks, update_cache, areebapache, _____________, __, oo, _______, ______________, ok, ________________________, ____________, recap, default, index, format, quiz, expanding, stack, custom, html, mysql, mariadb, php, cgi, cli, module, libapache, mod, restart, handlers, restarted, template, src, dest, var, www, references, https, playbooks_variables, collections, builtin, github, examples, devel, reference_appendices, yamlsyntax, playbooks_keywords,