Files
ansible-lemp-wordpress/playbooks/lemp-wordpress.yml
Sebastian Palencsar 9eac2c2057 fix: add preflight tasks to ensure /var/www/html exists before package installation
- Add preflight tasks to create web directory structure first
- Install nginx first as a base package
- Add HOME environment for all wp commands to prevent 'wrong path' errors
- Make auto-update settings ignore errors (WP not required to be installed)
2026-05-06 13:31:26 +02:00

464 lines
13 KiB
YAML
Executable File

---
# Ubuntu/Debian LEMP WordPress installation
- name: Install LEMP stack with WordPress (Ubuntu/Debian)
hosts: all
become: yes
pre_tasks:
- name: Gather OS facts
setup:
gather_subset:
- '!all'
- '!min'
- 'distribution'
- 'distribution_version'
- 'os_family'
- name: Load Debian family variables
include_vars: "../vars/debian-family.yml"
- name: Debug OS information
debug:
msg: |
OS Family: {{ ansible_os_family }}
Distribution: {{ ansible_distribution }}
Version: {{ ansible_distribution_version }}
Supported: Ubuntu/Debian family systems
- name: Verify supported OS
fail:
msg: "This playbook only supports Ubuntu/Debian systems. Detected: {{ ansible_os_family }}"
when: ansible_os_family != "Debian"
# Environment Detection and Configuration
- name: Detect if running in Docker
stat:
path: /.dockerenv
register: docker_env
tags: always
- name: Check for deployment_environment override
set_fact:
environment_override: "{{ deployment_environment is defined }}"
tags: always
- name: Set deployment environment (auto-detect or override)
set_fact:
deployment_environment: >-
{{ deployment_environment | default('docker' if docker_env.stat.exists else 'bare_metal') }}
tags: always
- name: Set WordPress site URL with smart defaults
set_fact:
wp_site_url: "{{ wp_site_url | default(_auto_wp_url) }}"
vars:
_auto_wp_url: >-
{{ 'http://localhost:8080' if (deployment_environment == 'docker')
else 'http://' + (ansible_default_ipv4.address | default('localhost')) }}
tags: always
- name: Debug deployment configuration
debug:
msg: |
Environment: {{ deployment_environment }} ({{ 'overridden' if environment_override else 'auto-detected' }})
WordPress URL: {{ wp_site_url }}
Docker detected: {{ docker_env.stat.exists }}
tags: always
vars:
# WordPress Configuration
wordpress_path: "{{ web_root }}"
wordpress_url: "http://{{ ansible_default_ipv4.address | default('localhost') }}"
# Database Configuration (use inventory variables or these defaults)
default_mysql_root_password: "{{ mysql_root_password | default('secure_root_password_123') }}"
default_wordpress_db_name: "{{ wordpress_db_name | default('wordpress_db') }}"
default_wordpress_db_user: "{{ wordpress_db_user | default('wordpress_user') }}"
default_wordpress_db_password: "{{ wordpress_db_password | default('secure_wp_password_123') }}"
# WordPress Admin (use inventory variables directly)
# These variables come from inventory/production.yml
# wp_admin_user, wp_admin_password, wp_admin_email, wp_site_title
# Auto-Update Configuration
update_cron_enabled: true
tasks:
# IMPORTANT: Pre-flight checks - create basic directory structure FIRST
- name: Ensure /var/www exists
file:
path: /var/www
state: directory
mode: '0755'
tags: [preflight]
- name: Create web root directory
file:
path: /var/www/html
state: directory
owner: www-data
group: www-data
mode: '0755'
tags: [preflight]
# Package Installation
- name: Install nginx first (required for web root)
apt:
name: nginx
state: present
tags: [packages, nginx]
- name: Install gnupg for PPA (required for apt-add-repository)
package:
name: gnupg
state: present
tags: packages
- name: Add PHP PPA for Ubuntu (needed for PHP 8.4)
apt_repository:
repo: ppa:ondrej/php
when: ansible_distribution == "Ubuntu"
tags: packages
- name: Install all required packages
package:
name: "{{ packages }}"
state: present
tags: packages
# Service Management
- name: Start and enable Nginx
service:
name: "{{ nginx_service }}"
state: started
enabled: yes
tags: nginx
- name: Start and enable MySQL/MariaDB
service:
name: "{{ mysql_service }}"
state: started
enabled: yes
tags: mysql
- name: Start and enable PHP-FPM
service:
name: "{{ php_fpm_service }}"
state: started
enabled: yes
tags: php
# Firewall Configuration
- name: Configure firewall (UFW)
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
tags: firewall
# MySQL Security (Ubuntu 24.04 fix for auth_socket)
- name: Set MySQL root password (using auth_socket first)
mysql_user:
name: root
password: "{{ mysql_root_password }}"
plugin: mysql_native_password
login_unix_socket: /var/run/mysqld/mysqld.sock
state: present
become: true
tags: mysql
- name: Create MySQL configuration for root
template:
src: ../templates/my.cnf.j2
dest: /root/.my.cnf
mode: '0600'
tags: mysql
- name: Remove anonymous MySQL users
mysql_user:
name: ""
host_all: yes
state: absent
tags: mysql
- name: Remove MySQL test database
mysql_db:
name: test
state: absent
tags: mysql
# WordPress Database Setup
- name: Create WordPress database
mysql_db:
name: "{{ wordpress_db_name }}"
state: present
tags: mysql
- name: Create WordPress database user
mysql_user:
name: "{{ wordpress_db_user }}"
password: "{{ wordpress_db_password }}"
priv: "{{ wordpress_db_name }}.*:ALL"
host: localhost
state: present
tags: mysql
# WordPress Installation
- name: Create web root directory
file:
path: "{{ wordpress_path }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
tags: wordpress
- name: Remove default nginx index file
file:
path: "{{ wordpress_path }}/index.nginx-debian.html"
state: absent
tags: wordpress
- name: Download WordPress
get_url:
url: https://wordpress.org/latest.tar.gz
dest: /tmp/wordpress.tar.gz
mode: '0644'
tags: wordpress
- name: Extract WordPress
unarchive:
src: /tmp/wordpress.tar.gz
dest: /tmp/
remote_src: yes
tags: wordpress
- name: Copy WordPress files
command: "cp -r /tmp/wordpress/. {{ wordpress_path }}/"
args:
creates: "{{ wordpress_path }}/wp-config-sample.php"
tags: wordpress
- name: Set WordPress file permissions
file:
path: "{{ wordpress_path }}"
owner: "{{ nginx_user }}"
group: "{{ nginx_user }}"
recurse: yes
tags: wordpress
# SSL Configuration (optional)
- name: Generate self-signed SSL certificate (if SSL enabled but no cert provided)
command: >
openssl req -x509 -nodes -days 365 -newkey rsa:2048
-keyout {{ ssl_cert_key_path | default('/etc/ssl/private/nginx-selfsigned.key') }}
-out {{ ssl_cert_path | default('/etc/ssl/certs/nginx-selfsigned.crt') }}
-subj "/C=DE/ST=State/L=City/O=Organization/OU=OrgUnit/CN={{ domain_name }}"
when:
- ssl_enabled | default(false)
- not (ssl_cert_path is defined and ssl_cert_key_path is defined)
tags: ssl
# Nginx Configuration
- name: Configure Nginx for WordPress
template:
src: >-
../templates/{% if ssl_enabled | default(false) %}wordpress-ssl.nginx.j2{%
else %}wordpress.nginx.j2{% endif %}
dest: "{{ nginx_sites_available }}/wordpress"
notify: restart nginx
tags: nginx
- name: Enable WordPress site
file:
src: "{{ nginx_sites_available }}/wordpress"
dest: "{{ nginx_sites_enabled }}/wordpress"
state: link
notify: restart nginx
tags: nginx
- name: Remove default Nginx site
file:
path: "{{ nginx_sites_enabled }}/default"
state: absent
notify: restart nginx
tags: nginx
# PHP Configuration
- name: Configure PHP-FPM pool
template:
src: ../templates/www.conf.j2
dest: "{{ php_fpm_pool_path }}/www.conf"
backup: yes
notify: restart php-fpm
tags: php
- name: Configure PHP settings
lineinfile:
path: "{{ php_cli_config_path }}/php.ini"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
backup: yes
loop:
- { regexp: '^upload_max_filesize', line: 'upload_max_filesize = 64M' }
- { regexp: '^post_max_size', line: 'post_max_size = 64M' }
- { regexp: '^memory_limit', line: 'memory_limit = 256M' }
- { regexp: '^max_execution_time', line: 'max_execution_time = 300' }
notify: restart php-fpm
tags: php
# WordPress Configuration
- name: Configure WordPress
template:
src: ../templates/wp-config.php.j2
dest: "{{ wordpress_path }}/wp-config.php"
owner: www-data
group: www-data
mode: '0600'
tags: wordpress
# WP-CLI Installation
- name: Download WP-CLI
get_url:
url: https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
dest: /usr/local/bin/wp
mode: '0755'
tags: wp-cli
- name: Verify WP-CLI installation
command: wp --info
environment:
HOME: "{{ wordpress_path }}"
register: wp_cli_info
tags: wp-cli
- name: Display WP-CLI info
debug:
var: wp_cli_info.stdout_lines
tags: wp-cli
# WordPress Core Installation
- name: Install WordPress Core
command: >
wp core install
--url="{{ wp_site_url }}"
--title="{{ wp_site_title }}"
--admin_user="{{ wp_admin_user }}"
--admin_password="{{ wp_admin_password }}"
--admin_email="{{ wp_admin_email }}"
--path="{{ wordpress_path }}"
--allow-root
environment:
HOME: "{{ wordpress_path }}"
tags: wp-install
- name: Set WordPress file ownership after installation
file:
path: "{{ wordpress_path }}"
owner: "{{ nginx_user }}"
group: "{{ nginx_user }}"
recurse: yes
tags: wp-install
- name: Display WordPress installation success
debug:
msg: |
WordPress installation completed!
URL: {{ wp_site_url }}
Admin User: {{ wp_admin_user }}
Admin Password: {{ wp_admin_password }}
Admin Email: {{ wp_admin_email }}
tags: wp-install
# WordPress Auto-Update Setup
- name: Create update script directory
file:
path: /var/backups/wordpress
state: directory
mode: '0755'
tags: update
- name: Deploy WordPress update script
copy:
src: ../scripts/wp-update.sh
dest: /usr/local/bin/wp-update.sh
mode: '0755'
tags: update
- name: Configure WordPress auto-update settings
command: >
wp option update auto_update_core_minor enabled
--allow-root
environment:
HOME: "{{ wordpress_path }}"
ignore_errors: yes
tags: update
- name: Set up cron job for WordPress updates
cron:
name: wordpress-weekly-update
job: "/usr/local/bin/wp-update.sh --minor --no-backup >> /var/log/wp-update.log 2>&1"
minute: "0"
hour: "4"
weekday: "0"
user: "root"
when: update_cron_enabled | default(true) | bool
tags: update
# Fail2Ban Security
- name: Deploy Fail2Ban configuration
template:
src: ../templates/fail2ban.j2
dest: /etc/fail2ban/jail.local
mode: '0644'
notify: restart fail2ban
tags: fail2ban
- name: Deploy WordPress Fail2Ban filter
copy:
src: ../templates/fail2ban-wordpress-filter.conf
dest: /etc/fail2ban/filter.d/wordpress-login.conf
mode: '0644'
notify: restart fail2ban
tags: fail2ban
- name: Ensure Fail2Ban service is running
service:
name: fail2ban
state: started
enabled: yes
tags: fail2ban
- name: Display Fail2Ban status info
debug:
msg: |
Fail2Ban security enabled!
Jails: sshd, nginx-http-auth, nginx-botsearch, wordpress-login, recidive
Use: fail2ban-client status to see banned IPs
tags: fail2ban
handlers:
- name: restart nginx
service:
name: "{{ nginx_service }}"
state: restarted
- name: restart php-fpm
service:
name: "{{ php_fpm_service }}"
state: restarted
- name: restart fail2ban
service:
name: fail2ban
state: restarted
- name: restart mysql
service:
name: "{{ mysql_service }}"
state: restarted