Add .gitignore file to exclude sensitive and temporary files. Update ansible.cfg to set default stdout callback and disable deprecation warnings. Modify hosts file to include a local group for localhost. Create local-playbook.yml for local development setup with pre-tasks and role execution. Enhance README.md with vault password setup instructions and debug output configuration. Update group_vars to include ansible_debug_output variable. Refactor roles to improve package installation checks and streamline Docker setup with GPG key management.

This commit is contained in:
ilia 2025-08-29 13:58:06 +00:00
parent e3d93ca4c8
commit 8a1b8609b7
22 changed files with 278 additions and 196 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Vault password file - NEVER commit this!
.ansible-vault-pass
~/.ansible-vault-pass
# Temporary files
*.tmp
*.bak
*~
# Python bytecode
__pycache__/
*.py[cod]
*$py.class
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db

View File

@ -34,11 +34,28 @@ This Ansible playbook automates the setup of development environments across mul
ansible-galaxy collection install -r collections/requirements.yml
```
### Vault Password Setup
Host variables are encrypted with Ansible Vault. You have two options:
#### Option 1: Vault Password File (Recommended)
Create a vault password file:
```bash
# Create the vault password file
echo "your_vault_password" > ~/.ansible-vault-pass
chmod 600 ~/.ansible-vault-pass
```
#### Option 2: Interactive Password Prompt
Use `--ask-vault-pass` with each command to be prompted for the vault password.
### Basic Setup
```bash
# Run on all development machines
# Run on all development machines (with vault password file)
ansible-playbook dev-playbook.yml
# Run on all development machines (interactive vault password)
ansible-playbook dev-playbook.yml --ask-vault-pass
# Run on specific host
ansible-playbook dev-playbook.yml --limit devVM
@ -68,6 +85,20 @@ Add `skip_reboot=true` to host variables:
bottom ansible_host=10.0.10.156 ansible_user=beast skip_reboot=true
```
### Debug Output
Control debug information display with the `ansible_debug_output` variable:
```bash
# Default: No debug output (clean, production-ready output)
ansible-playbook dev-playbook.yml --limit devVM
# Enable debug output (shows detailed status information)
ansible-playbook dev-playbook.yml --limit devVM -e "ansible_debug_output=true"
# Set permanently in group_vars/all.yml
ansible_debug_output: true
```
### Dry Run
```bash
# Check what would be changed
@ -82,6 +113,7 @@ ansible-playbook dev-playbook.yml -v
### Global Variables (`group_vars/all.yml`)
- `timezone`: System timezone (default: UTC)
- `locale`: System locale (default: en_US.UTF-8)
- `ansible_debug_output`: Show debug information (default: false)
- `fail2ban_bantime`: Ban duration in seconds
- `fail2ban_findtime`: Time window for failures
- `fail2ban_maxretry`: Max failures before ban

View File

@ -4,8 +4,10 @@ host_key_checking = True
timeout = 30
gathering = smart
fact_caching = memory
stdout_callback = yaml
stdout_callback = default
deprecation_warnings = False
vault_password_file = ~/.ansible-vault-pass
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o IdentitiesOnly=yes
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True

View File

@ -4,6 +4,9 @@ timezone: UTC
locale: en_US.UTF-8
ansible_python_interpreter: /usr/bin/python3
# Debug settings
ansible_debug_output: false
# Security settings
fail2ban_bantime: 3600
fail2ban_findtime: 600

3
hosts
View File

@ -14,3 +14,6 @@ debianDesktopVM ansible_host=10.0.10.206 ansible_user=user skip_reboot=true
[ansible]
ansible-controlVM ansible_host=localhost ansible_user=master
[local]
localhost ansible_connection=local

24
local-playbook.yml Normal file
View File

@ -0,0 +1,24 @@
- hosts: localhost
connection: local
become: true
roles:
- { role: maintenance, tags: ['maintenance'] }
- { role: base, tags: ['base', 'security'] }
- { role: user, tags: ['user'] }
- { role: ssh, tags: ['ssh', 'security'] }
- { role: shell, tags: ['shell'] }
- { role: development, tags: ['development', 'dev'] }
- { role: docker, tags: ['docker'] }
- { role: applications, tags: ['applications', 'apps'] }
- { role: snap, tags: ['snap', 'apps'] }
pre_tasks:
- name: Update apt cache
apt:
update_cache: yes
tasks:
- name: Display completion message
debug:
msg: "Local development environment setup completed successfully!"

View File

@ -0,0 +1 @@
---

View File

@ -0,0 +1 @@
---

View File

@ -1,13 +1,20 @@
---
- name: Check if desktop applications are installed
apt:
list: "{{ item }}"
register: app_check
loop:
- redshift
- libreoffice
- evince
- name: Check if applications are already installed
package_facts:
manager: apt
- name: Check if Brave browser is installed
command: brave-browser --version
register: brave_check
ignore_errors: true
changed_when: false
failed_when: false
no_log: true
- name: Set installation conditions
set_fact:
desktop_apps_needed: "{{ ['redshift', 'libreoffice', 'evince'] | difference(ansible_facts.packages.keys()) | length > 0 }}"
brave_needs_install: "{{ brave_check.rc != 0 or 'brave-browser' not in ansible_facts.packages }}"
- name: Install desktop applications
apt:
@ -16,63 +23,44 @@
- libreoffice
- evince
state: present
when:
- app_check.results[0].installed is not defined or
- app_check.results[1].installed is not defined or
- app_check.results[2].installed is not defined
when: desktop_apps_needed
- name: Check if Brave is already installed
command: brave-browser --version
register: brave_check
ignore_errors: true
changed_when: false
- name: Brave browser installation
block:
- name: Remove old Brave repository files
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/brave-browser.list
- /etc/apt/sources.list.d/brave-browser-release.sources
- name: Check if Brave package is installed via apt
apt:
list: brave-browser
register: brave_apt_check
changed_when: false
- name: Download Brave APT key
get_url:
url: https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
dest: /usr/share/keyrings/brave-browser-archive-keyring.gpg
mode: '0644'
- name: Remove old Brave repository files
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/brave-browser.list
- /etc/apt/sources.list.d/brave-browser-release.sources
when: brave_check.rc != 0 or brave_apt_check.results[0].installed is not defined
- name: Add Brave repository
apt_repository:
repo: "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"
filename: brave-browser
state: present
- name: Download Brave APT key
get_url:
url: https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
dest: /usr/share/keyrings/brave-browser-archive-keyring.gpg
mode: '0644'
when: brave_check.rc != 0 or brave_apt_check.results[0].installed is not defined
- name: Install Brave browser
apt:
name: brave-browser
state: present
- name: Add Brave repo (all Debian family)
apt_repository:
repo: "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"
filename: brave-browser
state: present
when: brave_check.rc != 0 or brave_apt_check.results[0].installed is not defined
- name: Update apt cache after Brave repo add
apt:
update_cache: yes
when: brave_check.rc != 0 or brave_apt_check.results[0].installed is not defined
- name: Install Brave browser
apt:
name: brave-browser
state: present
when: brave_check.rc != 0 or brave_apt_check.results[0].installed is not defined
when: brave_needs_install
- name: Display application status
debug:
msg:
- "Redshift installed: {{ 'Yes' if app_check.results[0].installed is defined else 'No' }}"
- "LibreOffice installed: {{ 'Yes' if app_check.results[1].installed is defined else 'No' }}"
- "Evince installed: {{ 'Yes' if app_check.results[2].installed is defined else 'No' }}"
- "Brave already installed: {{ brave_check.stdout if brave_check.rc == 0 else 'Not found' }}"
- "Brave package installed: {{ 'Yes' if brave_apt_check.results[0].installed is defined else 'No' }}"
- "Actions taken: {{ 'None - All apps already present' if app_check.results[0].installed is defined and app_check.results[1].installed is defined and app_check.results[2].installed is defined and brave_check.rc == 0 and brave_apt_check.results[0].installed is defined else 'Some applications installed/updated' }}"
- "Desktop apps needed: {{ desktop_apps_needed }}"
- "Brave needed: {{ brave_needs_install }}"
- "Redshift: {{ 'Installed' if 'redshift' in ansible_facts.packages else 'Missing' }}"
- "LibreOffice: {{ 'Installed' if 'libreoffice' in ansible_facts.packages else 'Missing' }}"
- "Evince: {{ 'Installed' if 'evince' in ansible_facts.packages else 'Missing' }}"
- "Brave: {{ brave_check.stdout if brave_check.rc == 0 else 'Not installed' }}"
when: ansible_debug_output | default(false) | bool

View File

@ -1,27 +1,19 @@
---
- name: Install base packages
- name: Install base system packages
apt:
name:
# Base utilities
- htop
- curl
- wget
- unzip
- xclip
state: present
update_cache: yes
- name: Install admin tools
apt:
name:
# Network and admin tools
- net-tools
- ufw
- fail2ban
- mailutils
state: present
- name: Install monitoring tools
apt:
name:
# Monitoring tools
- iotop
- nethogs
- logwatch
@ -43,5 +35,4 @@
- name: Configure locale
locale_gen:
name: "{{ locale | default('en_US.UTF-8') }}"
state: present
state: present

View File

@ -1,21 +1,13 @@
---
- name: Install dev packages
- name: Install development packages
apt:
name:
# Development tools
- git
- nodejs
state: present
- name: Install build tools
apt:
name:
- npm
# Build tools
- build-essential
- python3
- python3-pip
state: present
- name: Install npm
apt:
name: npm
state: present
state: present

View File

@ -6,112 +6,90 @@
- "Distribution Release: {{ ansible_facts['distribution_release'] }}"
- "Distribution Version: {{ ansible_facts['distribution_version'] }}"
- "OS Family: {{ ansible_facts['os_family'] }}"
when: ansible_debug_output | default(false) | bool
- name: Check if Docker is already installed
command: docker --version
register: docker_check
ignore_errors: true
changed_when: false
failed_when: false
no_log: true
- name: Check if Docker packages are installed via apt
apt:
list: docker-ce
package_facts:
manager: apt
register: docker_apt_check
changed_when: false
- name: Install Docker requirements
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: Set installation condition
set_fact:
docker_needs_install: "{{ docker_check.rc != 0 or 'docker-ce' not in ansible_facts.packages }}"
- name: Remove old Docker repository files
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/docker.list
- /etc/apt/sources.list.d/docker-ce.list
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Docker installation tasks
block:
- name: Install Docker requirements
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
- name: Create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Remove old Docker repository files
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/docker.list
- /etc/apt/sources.list.d/docker-ce.list
- name: Download Docker's official GPG key
get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.gpg
mode: '0644'
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Add Docker repository for Ubuntu
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
when:
- ansible_facts['distribution'] == "Ubuntu"
- docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Setup Docker GPG key
include_tasks: setup_gpg_key.yml
- name: Add Docker repository for Debian
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
state: present
when:
- ansible_facts['distribution'] == "Debian"
- docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Setup Docker repository
include_tasks: "setup_repo_{{ ansible_facts['distribution'] | lower | replace(' ', '_') }}.yml"
- name: Add Docker repository for Linux Mint
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
when:
- ansible_facts['distribution'] == "Linux Mint"
- docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Install Docker CE
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
- name: Update apt cache after Docker repo add
apt:
update_cache: yes
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Start and enable Docker service
systemd:
name: docker
state: started
enabled: yes
- name: Install Docker CE
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: yes
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Set target user variable
set_fact:
target_user: "{{ ansible_user | default(ansible_user_id) }}"
- name: Start and enable Docker service
systemd:
name: docker
state: started
enabled: yes
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
- name: Add user to docker group
user:
name: "{{ target_user }}"
groups: docker
append: yes
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
when: docker_check.rc != 0 or docker_apt_check.results[0].installed is not defined
when: docker_needs_install
- name: Display Docker status
debug:
msg:
- "Docker already installed: {{ docker_check.stdout if docker_check.rc == 0 else 'Not found' }}"
- "Docker CE package installed: {{ 'Yes' if docker_apt_check.results[0].installed is defined else 'No' }}"
- "Actions taken: {{ 'None - Docker already present' if docker_check.rc == 0 and docker_apt_check.results[0].installed is defined else 'Docker installation/configuration performed' }}"
- "Docker CE package installed: {{ 'Yes' if 'docker-ce' in ansible_facts.packages else 'No' }}"
- "Actions taken: {{ 'None - Docker already present' if not docker_needs_install else 'Docker installation/configuration performed' }}"
when: ansible_debug_output | default(false) | bool

View File

@ -0,0 +1,19 @@
---
- name: Download Docker's official GPG key
get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /tmp/docker.gpg
mode: '0644'
- name: Convert and install Docker GPG key
shell: gpg --dearmor < /tmp/docker.gpg > /etc/apt/keyrings/docker.gpg
- name: Set permissions on Docker GPG key
file:
path: /etc/apt/keyrings/docker.gpg
mode: '0644'
- name: Clean up temporary GPG key file
file:
path: /tmp/docker.gpg
state: absent

View File

@ -0,0 +1,6 @@
---
- name: Add Docker repository for Debian
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable"
state: present
update_cache: yes

View File

@ -0,0 +1,10 @@
---
- name: Set Ubuntu codename for Linux Mint
set_fact:
ubuntu_codename: "{{ 'jammy' if ansible_distribution_version is version('22', '>=') else 'focal' if ansible_distribution_version is version('21', '>=') else 'focal' if ansible_distribution_version is version('20', '>=') else 'bionic' }}"
- name: Add Docker repository for Linux Mint (using Ubuntu base)
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ubuntu_codename }} stable"
state: present
update_cache: yes

View File

@ -0,0 +1,6 @@
---
- name: Add Docker repository for Ubuntu
apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
update_cache: yes

View File

@ -1,11 +1 @@
---
# handlers file for maintenance
- name: restart fail2ban
systemd:
name: fail2ban
state: restarted
- name: reload ufw
ufw:
state: reloaded

View File

@ -1,8 +1,4 @@
---
- name: Update apt cache
apt:
update_cache: yes
- name: Upgrade all packages
apt:
upgrade: dist

View File

@ -1,4 +1,8 @@
---
- name: Set target user variable
set_fact:
target_user: "{{ ansible_user | default(ansible_user_id) }}"
- name: Install shell packages
apt:
name:
@ -8,29 +12,29 @@
- name: Set zsh as default shell for user
user:
name: "{{ ansible_user }}"
name: "{{ target_user }}"
shell: /usr/bin/zsh
- name: Install Oh My Zsh for user
become: true
become_user: "{{ ansible_user }}"
become_user: "{{ target_user }}"
shell: sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" "" --unattended
args:
creates: "/home/{{ ansible_user }}/.oh-my-zsh"
creates: "/home/{{ target_user }}/.oh-my-zsh"
- name: Clone Powerlevel10k theme
git:
repo: https://github.com/romkatv/powerlevel10k.git
dest: "/home/{{ ansible_user }}/.oh-my-zsh/custom/themes/powerlevel10k"
dest: "/home/{{ target_user }}/.oh-my-zsh/custom/themes/powerlevel10k"
depth: 1
update: no
become: true
become_user: "{{ ansible_user }}"
become_user: "{{ target_user }}"
- name: Deploy .zshrc for user
copy:
src: files/.zshrc
dest: "/home/{{ ansible_user }}/.zshrc"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
dest: "/home/{{ target_user }}/.zshrc"
owner: "{{ target_user }}"
group: "{{ target_user }}"
mode: '0644'

View File

@ -0,0 +1 @@
---

View File

@ -4,10 +4,17 @@
name: openssh-server
state: present
- name: Configure firewalls
- name: Configure firewalls - allow SSH port
ufw:
rule: allow
port: '22'
proto: tcp
- name: Configure firewalls - allow SSH by name (backup)
ufw:
rule: allow
name: OpenSSH
ignore_errors: true
- name: Enable UFW with deny default policy
ufw:

View File

@ -1,5 +1,10 @@
---
- name: Set target user variable
set_fact:
target_user: "{{ ansible_user | default(ansible_user_id) }}"
- name: Ensure user exists
user:
name: "{{ ansible_user }}"
name: "{{ target_user }}"
state: present
when: ansible_connection != 'local'