rdp-client/ansible/playbook.yml

650 lines
22 KiB
YAML

---
# ansible/playbook.yml
# RDP Thin Client Setup - Main Playbook v14
- name: Setup RDP Thin Client
hosts: rdp_clients
become: yes
tasks:
# Non-free Repos aktivieren
- name: Enable non-free and contrib repositories
lineinfile:
path: /etc/apt/sources.list
regexp: '^deb http://deb.debian.org/debian/ {{ ansible_distribution_release }} main$'
line: 'deb http://deb.debian.org/debian/ {{ ansible_distribution_release }} main contrib non-free non-free-firmware'
backrefs: yes
when: ansible_distribution == 'Debian'
ignore_errors: yes
- name: Enable non-free and contrib for security updates
lineinfile:
path: /etc/apt/sources.list
regexp: '^deb http://security.debian.org/debian-security {{ ansible_distribution_release }}-security main$'
line: 'deb http://security.debian.org/debian-security {{ ansible_distribution_release }}-security main contrib non-free non-free-firmware'
backrefs: yes
when: ansible_distribution == 'Debian'
ignore_errors: yes
# === BASE SYSTEM ===
- name: Update APT cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Ensure sudo is installed
apt:
name: sudo
state: present
update_cache: yes
become: yes
- name: Remove PulseAudio if present (conflicts with PipeWire)
apt:
name:
- pulseaudio
- pulseaudio-utils
state: absent
ignore_errors: yes
- name: Install base packages
apt:
name: "{{ item }}"
state: present
loop: "{{ base_packages }}"
ignore_errors: yes
- name: Ensure util-linux is installed (provides lsblk)
apt:
name: util-linux
state: present
- name: Install udiskie for USB automount
apt:
name: udiskie
state: present
- name: Update APT cache again for emoji fonts
apt:
update_cache: yes
- name: Install Emoji fonts for GUI
apt:
name: fonts-noto-color-emoji
state: present
- name: Install FreeRDP (try multiple package names for Debian 12/13 compatibility)
apt:
name: "{{ item }}"
state: present
loop:
- freerdp3-x11
- freerdp2-x11
ignore_errors: yes
- name: Find FreeRDP binary location
shell: which xfreerdp 2>/dev/null || which xfreerdp3 2>/dev/null || echo "not_found"
register: freerdp_location
changed_when: false
- name: Create xfreerdp symlink if only xfreerdp3 exists
file:
src: /usr/bin/xfreerdp3
dest: /usr/bin/xfreerdp
state: link
when: "'xfreerdp3' in freerdp_location.stdout and 'not_found' not in freerdp_location.stdout"
ignore_errors: yes
- name: Display FreeRDP installation status
debug:
msg: "FreeRDP found at: {{ freerdp_location.stdout }}"
- name: Create thin client user
user:
name: "{{ thin_client_user }}"
password: "{{ thin_client_password }}"
shell: /bin/bash
groups: audio,video,bluetooth,plugdev,netdev,sudo
append: yes
- name: Allow rdpuser to use sudo for package installation
lineinfile:
path: /etc/sudoers.d/rdpuser
line: '{{ thin_client_user }} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /sbin/reboot, /sbin/shutdown'
create: yes
mode: '0440'
validate: 'visudo -cf %s'
- name: Create media directory for USB mounts
file:
path: /media/rdpuser
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Create udiskie config directory
file:
path: /home/{{ thin_client_user }}/.config/udiskie
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Configure udiskie to mount in /media/rdpuser
copy:
dest: /home/{{ thin_client_user }}/.config/udiskie/config.yml
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0644'
content: |
device_config:
- id_type: filesystem
mount_path: /media/rdpuser/{device_file}
- name: Create LightDM config directory
file:
path: /etc/lightdm/lightdm.conf.d
state: directory
mode: '0755'
- name: Configure auto-login for LightDM
copy:
dest: /etc/lightdm/lightdm.conf.d/50-autologin.conf
content: |
[Seat:*]
autologin-user={{ thin_client_user }}
autologin-user-timeout=0
user-session=openbox
# === OPENBOX CONFIGURATION ===
- name: Create openbox config directory
file:
path: /home/{{ thin_client_user }}/.config/openbox
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Configure Openbox autostart
copy:
dest: /home/{{ thin_client_user }}/.config/openbox/autostart
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
content: |
#!/bin/bash
#Nummernblock aktivieren
numlockx on &
# Auto-load display configuration
# Try to load the last saved profile, fallback to common, then auto-detect
if [ -f ~/.config/autorandr/last_profile ]; then
LAST_PROFILE=$(cat ~/.config/autorandr/last_profile)
autorandr --load "$LAST_PROFILE" 2>/dev/null || autorandr --change
else
autorandr --load common 2>/dev/null || autorandr --change
fi
# Deaktiviere Energiesparfunktionen
xset s off # Screensaver aus
xset -dpms # DPMS (Display Power Management) aus
xset s noblank # Kein Blank-Screen
# Start PipeWire
pipewire &
pipewire-pulse &
# Start Bluetooth
blueman-applet &
# Start USB automount (udiskie)
udiskie --tray --automount --notify &
# Session Watcher läuft als systemd-Service
# Start RDP Launcher
/usr/local/bin/rdp-launcher.sh &
- name: Configure Openbox menu (right-click context menu)
copy:
dest: /home/{{ thin_client_user }}/.config/openbox/menu.xml
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
content: |
<?xml version="1.0" encoding="UTF-8"?>
<openbox_menu xmlns="http://openbox.org/3.4/menu">
<menu id="root-menu" label="Openbox">
<item label="RDP Profile Manager">
<action name="Execute">
<command>/usr/local/bin/rdp-profile-manager.py</command>
</action>
</item>
<item label="Bluetooth Manager">
<action name="Execute">
<command>blueman-manager</command>
</action>
</item>
<item label="Datei Manager">
<action name="Execute">
<command>pcmanfm</command>
</action>
</item>
<item label="Sound Einstellungen">
<action name="Execute">
<command>pavucontrol</command>
</action>
</item>
<menu id="display-menu" label="Anzeigeeinstellungen">
<item label="Anzeige konfigurieren">
<action name="Execute">
<command>arandr</command>
</action>
</item>
<item label="Aktuelles Profil speichern">
<action name="Execute">
<command>sh -c 'profile=$(zenity --entry --title="Display Profil speichern" --text="Profilnamen eingeben:"); [ -n "$profile" ] &amp;&amp; autorandr --save "$profile" --force &amp;&amp; echo "$profile" &gt; ~/.config/autorandr/last_profile &amp;&amp; zenitiy --info --text="Profil gespeichert: $profile"'</command>
</action>
</item>
<item label="Profil laden">
<action name="Execute">
<command>sh -c 'profile=$(zenity --list --title="Display Profil laden" --column="Profile" $(autorandr --list));[ -n "$profile" ] &amp;&amp; autorandr --load "$profile" &amp;&amp; echo "$profile" &gt; ~/.config/autorandr/last_profile &amp;&amp; zenity --info --text="Dieses Profil wird automatisch beim nächsten Start geladen"'</command>
</action>
</item>
<item label="Delete Profile">
<action name="Execute">
<command>sh -c 'profile=$(zenity --list --title="Display Profil löschen" --text="Profile auswählen zum löschen:" --column="Profiles" $(autorandr --list));[ -n "$profile" ] &amp;&amp; zenity --question --title="Löschen bestätigen" --text="Delete profile: $profile?" &amp;&amp; autorandr --remove "$profile" &amp;&amp; zenity --info --text="Profil gelöscht: $profile"'</command></action></item>
<menu id="gpu-menu" label="Grafiktreiber"><item label="Install Intel Graphics Driver">
<action name="Execute">
<command>lxterminal -e "bash -c 'echo Installing Intel graphics driver...; sudo apt update &amp;&amp; sudo apt install -y xserver-xorg-video-intel firmware-misc-nonfree &amp;&amp; echo Done! Please reboot for changes to take effect.; read -p Press Enter to close...'"</command>
</action>
</item>
<item label="Install AMD/Radeon Driver">
<action name="Execute">
<command>lxterminal -e "bash -c 'echo Installing AMD/Radeon graphics driver...; sudo apt update &amp;&amp; sudo apt install -y firmware-amd-graphics xserver-xorg-video-amdgpu xserver-xorg-video-radeon &amp;&amp; echo Done! Please reboot for changes to take effect.; read -p Press Enter to close...'"</command>
</action>
</item>
<item label="Install NVIDIA Driver">
<action name="Execute">
<command>lxterminal -e "bash -c 'echo Installing NVIDIA proprietary driver...; sudo apt update &amp;&amp; sudo apt install -y nvidia-driver firmware-misc-nonfree &amp;&amp; echo Done! Please reboot for changes to take effect.; read -p Press Enter to close...'"</command>
</action>
</item>
<separator />
<item label="Show Current GPU Info">
<action name="Execute">
<command>lxterminal -e "bash -c 'echo === Graphics Card Info ===; lspci | grep -i vga; echo; echo === Loaded Driver ===; lsmod | grep -iE video|radeon|amdgpu|nouveau|nvidia|i915; echo; echo === Xorg Driver ===; grep -i driver /var/log/Xorg.0.log 2>/dev/null | tail -n 20; read -p Press Enter to close...'"</command>
</action>
</item>
</menu>
</menu><item label="Terminal">
<action name="Execute">
<command>lxterminal</command>
</action>
</item>
<separator />
<item label="Neustart">
<action name="Execute">
<command>systemctl reboot</command>
</action>
</item>
<item label="Ausschalten">
<action name="Execute">
<command>systemctl poweroff</command>
</action>
</item>
</menu>
</openbox_menu>
# === AUDIO CONFIGURATION ===
# PipeWire wird über Openbox autostart gestartet (siehe autostart config)
# Keine systemd user services nötig
# === BLUETOOTH CONFIGURATION ===
- name: Check if Bluetooth hardware is present
stat:
path: /sys/class/bluetooth
register: bluetooth_hw
- name: Enable Bluetooth service (if hardware present)
systemd:
name: bluetooth
enabled: yes
state: started
when: bluetooth_hw.stat.exists
ignore_errors: yes
# === SMART CARD CONFIGURATION ===
- name: Check if pcscd is installed
command: which pcscd
register: pcscd_installed
ignore_errors: yes
changed_when: false
- name: Enable pcscd service (socket-activated on demand)
systemd:
name: pcscd
enabled: yes
when: pcscd_installed.rc == 0
ignore_errors: yes
# === PROFILE DIRECTORY ===
- name: Create RDP profile directory
file:
path: "{{ profile_dir }}"
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Create empty profiles.ini if not exists
copy:
dest: "{{ profile_file }}"
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0644'
content: |
# RDP Profile Configuration
# Auto-generated by RDP Thin Client Setup
force: no
# === INSTALL PYTHON DEPENDENCIES ===
- name: Install python-xlib via pip
shell: pip3 install python-xlib --break-system-packages
args:
creates: /usr/local/lib/python3.11/dist-packages/Xlib
ignore_errors: yes
- name: Alternative - Install python-xlib from apt
apt:
name: python3-xlib
state: present
ignore_errors: yes
# === COPY SCRIPTS ===
- name: Copy RDP Profile Manager
copy:
src: ../files/rdp-profile-manager.py
dest: /usr/local/bin/rdp-profile-manager.py
mode: '0755'
- name: Copy RDP Launcher
copy:
src: ../files/rdp-launcher.sh
dest: /usr/local/bin/rdp-launcher.sh
mode: '0755'
- name: Copy Session Watcher
copy:
src: ../files/session-watcher.py
dest: /usr/local/bin/session-watcher.py
mode: '0755'
# === SESSION WATCHER SYSTEMD SERVICE ===
- name: Create systemd user service directory
file:
path: /home/{{ thin_client_user }}/.config/systemd/user
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Create Session Watcher systemd service
copy:
dest: /home/{{ thin_client_user }}/.config/systemd/user/session-watcher.service
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0644'
content: |
[Unit]
Description=RDP Session Watcher - Exit Hotkey Monitor
After=graphical.target
[Service]
Type=simple
ExecStart=/usr/local/bin/session-watcher.py
Restart=always
RestartSec=3
Environment=DISPLAY=:0
[Install]
WantedBy=default.target
- name: Create default.target.wants directory
file:
path: /home/{{ thin_client_user }}/.config/systemd/user/default.target.wants
state: directory
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
mode: '0755'
- name: Create symlink to enable Session Watcher service
file:
src: /home/{{ thin_client_user }}/.config/systemd/user/session-watcher.service
dest: /home/{{ thin_client_user }}/.config/systemd/user/default.target.wants/session-watcher.service
owner: "{{ thin_client_user }}"
group: "{{ thin_client_user }}"
state: link
force: yes
ignore_errors: yes
# === BRANDING ===
- name: Check if branding files exist
stat:
path: ../files/branding/boot-logo.png
register: branding_check
delegate_to: localhost
become: no
- name: Create branding directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /usr/share/pixmaps/hackersoft
- /boot/grub
when: branding_check.stat.exists
- name: Copy boot logo for Plymouth
copy:
src: ../files/branding/boot-logo.png
dest: /usr/share/pixmaps/hackersoft/boot-logo.png
mode: '0644'
when: branding_check.stat.exists
- name: Copy GRUB background
copy:
src: ../files/branding/grub-background.png
dest: /boot/grub/hackersoft-bg.png
mode: '0644'
when: branding_check.stat.exists
- name: Configure LightDM to use custom background
lineinfile:
path: /etc/default/grub
regexp: '^#?GRUB_BACKGROUND='
line: 'GRUB_BACKGROUND="/boot/grub/hackersoft-bg.png"'
when: branding_check.stat.exists
notify: Update GRUB
# === POWER MANAGEMENT DEAKTIVIEREN ===
- name: Disable systemd suspend/hibernate
systemd:
name: "{{ item }}"
masked: yes
loop:
- sleep.target
- suspend.target
- hibernate.target
- hybrid-sleep.target
ignore_errors: yes
- name: Create logind config directory
file:
path: /etc/systemd/logind.conf.d
state: directory
mode: '0755'
- name: Create systemd logind config to disable power management
copy:
dest: /etc/systemd/logind.conf.d/no-suspend.conf
content: |
[Login]
HandlePowerKey=ignore
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
IdleAction=ignore
mode: '0644'
notify: Restart systemd-logind
- name: Enable NumLock in LightDM
lineinfile:
path: /etc/lightdm/lightdm.conf
regexp: '^#?greeter-setup-script='
line: 'greeter-setup-script=/usr/bin/numlockx on'
create: yes
- name: Install Plymouth for boot splash
apt:
name:
- plymouth
- plymouth-themes
state: present
when: branding_check.stat.exists
- name: Create custom Plymouth theme directory
file:
path: /usr/share/plymouth/themes/hackersoft
state: directory
mode: '0755'
when: branding_check.stat.exists
- name: Create Plymouth theme configuration
copy:
dest: /usr/share/plymouth/themes/hackersoft/hackersoft.plymouth
content: |
[Plymouth Theme]
Name=HackerSoft
Description=HackerSoft RDP Thin Client Boot Splash
ModuleName=script
[script]
ImageDir=/usr/share/plymouth/themes/hackersoft
ScriptFile=/usr/share/plymouth/themes/hackersoft/hackersoft.script
mode: '0644'
when: branding_check.stat.exists
- name: Create Plymouth script
copy:
dest: /usr/share/plymouth/themes/hackersoft/hackersoft.script
content: |
logo.image = Image("boot-logo.png");
logo.sprite = Sprite(logo.image);
logo.opacity = 1.0;
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
logo_width = logo.image.GetWidth();
logo_height = logo.image.GetHeight();
logo.x = screen_width / 2 - logo_width / 2;
logo.y = screen_height / 2 - logo_height / 2;
logo.sprite.SetPosition(logo.x, logo.y, 0);
fun refresh_callback() {
logo.sprite.SetOpacity(logo.opacity);
}
Plymouth.SetRefreshFunction(refresh_callback);
mode: '0644'
when: branding_check.stat.exists
- name: Copy logo to Plymouth theme
copy:
src: ../files/branding/boot-logo.png
dest: /usr/share/plymouth/themes/hackersoft/boot-logo.png
mode: '0644'
when: branding_check.stat.exists
- name: Set Plymouth theme
command: plymouth-set-default-theme -R hackersoft
when: branding_check.stat.exists
ignore_errors: yes
- name: Create backgrounds directory if needed
file:
path: /usr/share/backgrounds
state: directory
mode: '0755'
when: branding_check.stat.exists
- name: Check if desktop-background.png exists
stat:
path: ../files/branding/desktop-background.png
register: desktop_bg_check
delegate_to: localhost
become: no
- name: Copy desktop background (dedicated file)
copy:
src: ../files/branding/desktop-background.png
dest: /usr/share/backgrounds/hackersoft-wallpaper.png
mode: '0644'
when: branding_check.stat.exists and desktop_bg_check.stat.exists
- name: Copy desktop background (fallback to login-background)
copy:
src: ../files/branding/login-background.png
dest: /usr/share/backgrounds/hackersoft-wallpaper.png
mode: '0644'
when: branding_check.stat.exists and not desktop_bg_check.stat.exists
- name: Install feh for wallpaper management
apt:
name: feh
state: present
when: branding_check.stat.exists
- name: Set Openbox wallpaper in autostart
lineinfile:
path: /home/{{ thin_client_user }}/.config/openbox/autostart
line: 'feh --bg-scale /usr/share/backgrounds/hackersoft-wallpaper.png &'
insertafter: '^fi'
when: branding_check.stat.exists
# === CLEANUP ===
- name: Remove unnecessary packages
apt:
name:
- gnome-*
- libreoffice-*
state: absent
autoremove: yes
ignore_errors: yes
- name: Disable unnecessary services
systemd:
name: "{{ item }}"
enabled: no
state: stopped
loop:
- ModemManager
- cups
ignore_errors: yes
- name: Final message
debug:
msg: "RDP Thin Client setup complete! Please reboot the system."
handlers:
- name: Update GRUB
command: update-grub
- name: Restart systemd-logind
systemd:
name: systemd-logind
state: restarted