/
/
/
This repo is destined for my server automations and setup.
1---
2# ============================================================================
3# Snapcast Client Deployment Playbook
4# ============================================================================
5#
6# This playbook deploys Snapcast clients to HiFi nodes in two stages:
7# 1. Generic server setup on all hifi-nodes (system, docker, security, user)
8# 2. Snapcast client configuration with individual parameters per node
9#
10# Usage:
11# ansible-playbook snapcast.yml
12#
13# ============================================================================
14
15# STAGE 1: Generic server setup on all hifi-nodes
16- name: "Generic Server Setup - HiFi Nodes Group"
17 hosts: hifi_nodes
18 become: true
19 gather_facts: true
20
21 # Load configuration from group_vars and host_vars
22 # Override with command line variables as needed
23
24 pre_tasks:
25 - name: Display deployment information
26 debug:
27 msg: |
28 ============================================================================
29 Generic Server Setup Starting - Stage 1
30 ============================================================================
31 Target Host: {{ inventory_hostname }}
32 Target IP: {{ ansible_default_ipv4.address | default('Unknown') }}
33 OS: {{ ansible_distribution | default('Unknown') }} {{ ansible_distribution_version | default('') }}
34 Architecture: {{ ansible_architecture | default('Unknown') }}
35 Server Type: {{ server_type | default('Unknown') }}
36 Node Type: {{ 'Kitchen' if inventory_hostname == 'hifi-node-kitchen' else 'Dining' }}
37 ============================================================================
38 tags: always
39
40 - name: Update apt cache
41 apt:
42 update_cache: yes
43 cache_valid_time: 3600
44 when: ansible_os_family == "Debian"
45
46 # ============================================================================
47 # GENERIC SERVER SETUP (System + User + Docker)
48 # ============================================================================
49
50 roles:
51 # System configuration (hostname, packages, sudo, etc.)
52 - role: system
53 tags: [system, base, configuration]
54
55 # Docker installation via geerlingguy.docker
56 - role: geerlingguy.docker
57 tags: [docker, containers]
58
59 # Docker directory framework (proper /docker permissions)
60 - role: docker-framework
61 tags: [docker, framework]
62
63 # Security hardening via geerlingguy.security
64 - role: geerlingguy.security
65 tags: [security, hardening]
66
67 # System monitoring
68 - role: monitoring
69 tags: [monitoring, glances]
70
71 tasks:
72 - name: Configure user with proper group memberships
73 include_role:
74 name: user
75 vars:
76 my_user_username: yannick
77 my_user_groups: "{{ user_groups | default(['docker', 'sudo']) }}"
78 my_user_ssh_public_key: "{{ user_ssh_keys[0] | default('') if user_ssh_keys is defined and user_ssh_keys|length > 0 else '' }}"
79 tags: [user, setup, configuration]
80
81 # ============================================================================
82 # POST-INSTALLATION VALIDATION
83 # ============================================================================
84
85 post_tasks:
86 - name: System validation
87 block:
88 - name: Check hostname
89 command: "hostname"
90 register: hostname_check
91 changed_when: false
92
93 - name: Check Docker installation
94 command: "docker --version"
95 register: docker_check
96 failed_when: false
97 changed_when: false
98
99 - name: Check Docker service status
100 systemd:
101 name: docker
102 register: docker_service
103 ignore_errors: yes
104
105 - name: Check SSH configuration
106 command: "sshd -t"
107 register: ssh_config_check
108 changed_when: false
109
110 - name: Check user in docker group
111 command: "groups yannick"
112 register: user_groups_check
113 changed_when: false
114
115 tags: [validation, system, snapcast]
116
117 - name: Display configuration summary
118 debug:
119 msg: |
120 ============================================================================
121 Generic Server Setup Complete! - Stage 1 Finished
122 ============================================================================
123
124 ð¥ï¸ System Information:
125 Hostname: {{ hostname_check.stdout | default('Unknown') }}
126 Target Host: {{ inventory_hostname }}
127 IP Address: {{ ansible_default_ipv4.address | default('Unknown') }}
128 OS: {{ ansible_distribution | default('Unknown') }} {{ ansible_distribution_version | default('') }}
129 Architecture: {{ ansible_architecture | default('Unknown') }}
130 Server Type: {{ server_type | default('Unknown') }}
131
132 ð³ Docker Configuration:
133 Docker Version: {{ docker_check.stdout | default('Not installed') }}
134 Docker Service: {{ 'Running' if docker_service is defined and docker_service.status.ActiveState == 'active' else 'Not running' }}
135 Docker Users: yannick
136
137 ð Security Configuration:
138 SSH Port: {{ security_ssh_port | default(22) }}
139 Password Auth: {{ security_ssh_password_authentication | default('yes') }}
140 Root Login: {{ security_ssh_permit_root_login | default('yes') }}
141 SSH Config: {{ 'â Valid' if ssh_config_check is defined and ssh_config_check.rc == 0 else 'â Skipped' if ssh_config_check is not defined else 'â Invalid' }}
142
143 ð¥ User Configuration:
144 Primary User: yannick
145 User Groups: {{ user_groups_check.stdout | default('Skipped') }}
146 Sudo Access: {{ 'Passwordless' if security_sudoers_passwordless is defined else 'Standard' }}
147
148 ð§ Validation Results:
149 Hostname Set: {{ 'â Success' if hostname_check is defined and hostname_check.rc == 0 else 'â Skipped' if hostname_check is not defined else 'â Failed' }}
150 Docker Installed: {{ 'â Success' if docker_check is defined and docker_check.rc == 0 else 'â Skipped' if docker_check is not defined else 'â Failed' }}
151 Docker Running: {{ 'â Running' if docker_service is defined and docker_service.status.ActiveState == 'active' else 'â Skipped' if docker_service is not defined else 'â Not running' }}
152 SSH Config Valid: {{ 'â Valid' if ssh_config_check is defined and ssh_config_check.rc == 0 else 'â Skipped' if ssh_config_check is not defined else 'â Invalid' }}
153
154 ð¡ Usage Examples:
155 SSH Login: ssh yannick@{{ ansible_default_ipv4.address | default(inventory_hostname) }}
156 Docker Commands: docker ps
157 System Status: systemctl status docker
158
159 ð Next Steps:
160 1. Test SSH access with your private key
161 2. Verify Docker functionality: docker run hello-world
162 3. Deploy additional services as needed
163
164 ============================================================================
165 tags: [always, snapcast]
166
167# STAGE 2: Snapcast client configuration with individual parameters
168- name: "Snapcast Client Configuration - Individual Node Setup"
169 hosts: hifi_nodes
170 become: true
171 gather_facts: false # No need to gather facts again
172
173 pre_tasks:
174 - name: Display snapcast configuration information
175 debug:
176 msg: |
177 ============================================================================
178 Snapcast Client Configuration - Stage 2
179 ============================================================================
180 Target Host: {{ inventory_hostname }}
181 Node Type: {{ 'Kitchen (mono mixing)' if inventory_hostname == 'hifi-node-kitchen' else 'Dining (stereo)' }}
182 Audio Device: {{ 'hw:1,0' if inventory_hostname == 'hifi-node-kitchen' else 'hw:0,0' }}
183 Mono Mixing: {{ 'Enabled' if inventory_hostname == 'hifi-node-kitchen' else 'Disabled' }}
184 Snapcast Server: {{ hostvars[groups['storage_servers'][0]].ansible_host | default('storage.home') }}
185 ============================================================================
186 tags: always
187
188 roles:
189 - role: snapcast-client
190 tags: [snapcast, audio, hifi]
191 vars:
192 # Configure based on node type
193 audio_merge_to_mono: "{{ inventory_hostname == 'hifi-node-kitchen' }}"
194 manage_asound_conf: "{{ inventory_hostname == 'hifi-node-kitchen' }}"
195
196 # Device configuration
197 audio_hw_device: "{{ 'hw:1,0' if inventory_hostname == 'hifi-node-kitchen' else 'hw:0,0' }}"
198 audio_ctl_card: "{{ 1 if inventory_hostname == 'hifi-node-kitchen' else 0 }}"
199 snapcast_device: "{{ 'hw:0,0' if inventory_hostname == 'hifi-node-dining' }}"
200
201 # Server configuration using inventory lookup
202 snapcast_server_host: "{{ hostvars[groups['storage_servers'][0]].ansible_host | default('storage.home') }}"
203 snapcast_client_name: "{{ inventory_hostname }}"
204
205 # Disable Avahi for explicit server configuration
206 enable_avahi: false
207
208 post_tasks:
209 - name: Validate snapcast installation
210 block:
211 - name: Check snapclient service status
212 systemd:
213 name: snapclient
214 register: snapclient_service
215
216 - name: Check snapclient configuration
217 command: "cat /etc/default/snapclient"
218 register: snapclient_config
219 changed_when: false
220
221 tags: [validation, snapcast]
222
223 - name: Display snapcast configuration summary
224 debug:
225 msg: |
226 ============================================================================
227 Snapcast Client Configuration Complete! - Stage 2 Finished
228 ============================================================================
229
230 ð Snapcast Configuration:
231 Client Name: {{ inventory_hostname }}
232 Server Host: {{ hostvars[groups['storage_servers'][0]].ansible_host | default('storage.home') }}
233 Audio Device: {{ 'hw:1,0 (mono mixed)' if inventory_hostname == 'hifi-node-kitchen' else 'hw:0,0 (stereo)' }}
234 Service Status: {{ 'Running' if snapclient_service is defined and snapclient_service.status.ActiveState == 'active' else 'Not running / Skipped' }}
235
236 ð§ Validation Results:
237 Snapcast Running: {{ 'â Running' if snapclient_service is defined and snapclient_service.status.ActiveState == 'active' else 'â Skipped' if snapclient_service is not defined else 'â Not running' }}
238
239 ð¡ Usage Examples:
240 Snapcast Status: systemctl status snapclient
241 Snapcast Logs: journalctl -u snapclient -f
242
243 ð Next Steps:
244 1. Test audio playback from HomeAssistant server
245 2. Verify mono mixing on kitchen node (if applicable)
246 3. Check sync between multiple clients
247
248 ============================================================================
249 tags: [always, snapcast]