feat(vaultwarden): add SMTP configuration options and enhance signup settings

- Introduced SMTP settings for Vaultwarden including host, port, security, and authentication details.
- Added variables for signup verification, 2FA settings, password hints, and logging options.
- Updated Vaultwarden deployment to utilize new SMTP configurations.
- Enhanced Grafana module to support dynamic dashboard and datasource provisioning.
- Added LLM proxy configuration for Open Web UI with necessary environment variables.
This commit is contained in:
2026-05-30 23:24:44 +02:00
parent 350650ecc2
commit ec740f458f
89 changed files with 3154 additions and 856 deletions
-33
View File
@@ -1,33 +0,0 @@
- name: Add ROCm repository
become: true
ansible.builtin.yum_repository:
name: rocm
description: ROCm Repository
baseurl: https://repo.radeon.com/rocm/el9/latest/main/
gpgcheck: true
gpgkey: https://repo.radeon.com/rocm/rocm.gpg.key
enabled: true
state: present
- name: Add AMD Graphics repository
become: true
ansible.builtin.yum_repository:
name: rocmgraphics
description: AMD Graphics Repository
baseurl: https://repo.radeon.com/graphics/latest/el/9.6/main/x86_64/
gpgkey: https://repo.radeon.com/rocm/rocm.gpg.key
gpgcheck: true
enabled: true
state: present
- name: Install rocm-dkms and dependencies
become: true
ansible.builtin.package:
name:
- rocm
- rocm-developer-tools
- hipblas-devel
- hip-devel
- rocwmma-devel
- rocm-opencl-devel
state: present
-129
View File
@@ -1,129 +0,0 @@
- name: Install dependencies
become: true
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- sysstat
- bc
- name: Enable sysstat service
become: true
ansible.builtin.systemd:
name: sysstat
enabled: true
state: started
- name: Read existing GRUB_CMDLINE_LINUX
ansible.builtin.shell: set -o pipefail && grep '^GRUB_CMDLINE_LINUX=' /etc/default/grub | cut -d= -f2- | tr -d '"'
register: grub_line
changed_when: false
failed_when: grub_line.stdout == ""
- name: Set fact for existing_cmdline
ansible.builtin.set_fact:
existing_cmdline: "{{ grub_line.stdout }}"
- name: Set fedora grub file locations
ansible.builtin.set_fact:
grub_config_path_bios: /boot/grub2/grub.cfg
when: ansible_distribution == "Fedora" and ansible_os_family == "RedHat"
- name: Show current GRUB_CMDLINE_LINUX
ansible.builtin.debug:
msg: "Current GRUB_CMDLINE_LINUX: {{ existing_cmdline }}"
- name: Updated GRUB_CMDLINE_LINUX
ansible.builtin.debug:
msg: "Current GRUB_CMDLINE_LINUX: {{ existing_cmdline | regex_replace(' *mem_sleep_default=\\S+', '') }} mem_sleep_default=deep"
- name: Ensure mem_sleep_default=deep is set in GRUB_CMDLINE_LINUX
become: true
vars:
grub_cmdline_linux_line: >-
GRUB_CMDLINE_LINUX="{{ existing_cmdline | regex_replace(' *mem_sleep_default=\\S+', '') }} mem_sleep_default=deep"
ansible.builtin.lineinfile:
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX='
line: "{{ grub_cmdline_linux_line }}"
- name: Generate grub config for BIOS
become: true
changed_when: false
ansible.builtin.command: grub2-mkconfig -o {{ grub_config_path_bios }}
- name: Create idle check and suspend script
become: true
ansible.builtin.copy:
dest: /usr/local/bin/auto_suspend_script.sh
mode: '0755'
owner: root
group: root
content: "{{ lookup('file', 'scripts/auto_suspend.sh') }}"
- name: Create systemd service for auto suspend
become: true
ansible.builtin.copy:
dest: /etc/systemd/system/auto-suspend.service
content: |
[Unit]
Description=Check system idleness and suspend if idle
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/auto_suspend_script.sh
owner: root
group: root
mode: '0644'
- name: Create sleep.conf.d folder if it doesn't exist
become: true
ansible.builtin.file:
path: /etc/systemd/sleep.conf.d
state: directory
owner: root
group: root
mode: '0755'
- name: Create sleep.conf.d for deep sleep
become: true
ansible.builtin.copy:
dest: /etc/systemd/sleep.conf.d/mem-deep.conf
content: |
[Sleep]
MemorySleepMode=deep
owner: root
group: root
mode: '0644'
- name: Create systemd timer for auto suspend
become: true
ansible.builtin.copy:
dest: /etc/systemd/system/auto-suspend.timer
content: |
[Unit]
Description=Run idle suspend check every 10 minutes
[Timer]
OnBootSec=10min
OnUnitActiveSec=10min
Unit=auto-suspend.service
[Install]
WantedBy=timers.target
owner: root
group: root
mode: '0644'
- name: Reload systemd daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
- name: Enable and start auto-suspend timer
become: true
ansible.builtin.systemd:
name: auto-suspend.timer
enabled: true
state: started
-146
View File
@@ -1,146 +0,0 @@
- name: Ensure podman user exists before model sync
ansible.builtin.include_tasks:
file: podman/user.yml
- name: Enable sudo access to podman user (temporary)
become: true
ansible.builtin.lineinfile:
path: "/etc/sudoers.d/ansible_{{ podman_user }}_tmp"
create: true
mode: "0440"
line: "{{ ansible_user_id }} ALL=({{ podman_user }}) NOPASSWD: ALL"
validate: "visudo -cf %s"
- name: Validate llama_models schema
ansible.builtin.assert:
that:
- __llama_models__ is iterable
fail_msg: "llama_models must be a list"
- name: Install Python packages required for llama model sync
become: true
ansible.builtin.package:
name: "{{ __llama_sync_system_packages__ }}"
state: present
vars:
__llama_sync_system_packages__: >-
{{
{
'Debian': ['python3-pip', 'python3-packaging', 'python3-venv'],
'RedHat': ['python3-pip', 'python3-packaging'],
'Archlinux': ['python-pip', 'python-packaging']
}[ansible_os_family]
}}
- name: Install Hugging Face hub client in llama model sync virtualenv
become: true
become_user: "{{ podman_user }}"
ansible.builtin.pip:
name:
- pip
- setuptools
- packaging
- huggingface_hub>=0.32.0
state: present
virtualenv: "{{ __llama_sync_venv__ }}"
virtualenv_command: "python3 -m venv"
- name: Create llama router model directories
become: true
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "{{ item.mode }}"
loop:
- { path: "{{ podman_user_home }}/models", mode: "0750" }
- { path: "{{ __llama_managed_dir__ }}", mode: "0750" }
- { path: "{{ __llama_links_dir__ }}", mode: "0750" }
- { path: "{{ __llama_router_dir__ }}", mode: "0750" }
- name: Copy llama Hugging Face sync helper
become: true
ansible.builtin.copy:
src: scripts/llama_hf_sync.py
dest: "{{ __llama_sync_script__ }}"
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0750"
- name: Write desired llama model manifest input
become: true
ansible.builtin.copy:
content: "{{ __llama_models__ | to_nice_json }}"
dest: "{{ __llama_models_input_file__ }}"
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0640"
- name: Write global llama preset options
become: true
ansible.builtin.copy:
content: "{{ __llama_preset_global__ | to_nice_json }}"
dest: "{{ __llama_preset_global_input_file__ }}"
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0640"
- name: Run llama model sync from Hugging Face
vars:
__llama_sync_argv__: >-
{{
[
__llama_sync_python__,
__llama_sync_script__,
'--models-file',
__llama_models_input_file__,
'--managed-dir',
__llama_managed_dir__,
'--links-dir',
__llama_links_dir__,
'--manifest-file',
__llama_manifest_file__,
'--preset-file',
__llama_preset_file__,
'--preset-global-file',
__llama_preset_global_input_file__,
'--container-links-dir',
'/models/managed-links'
]
+ (['--prune'] if llama_models_prune_enabled | default(true) else [])
+ (['--dry-run'] if llama_models_dry_run | default(false) else [])
}}
become: true
become_user: "{{ podman_user }}"
ansible.builtin.command:
argv: "{{ __llama_sync_argv__ }}"
environment:
HF_TOKEN: "{{ lookup('env', 'HF_TOKEN') | default('', true) }}"
register: __llama_sync_output__
changed_when: false
- name: Parse llama sync output
ansible.builtin.set_fact:
__llama_sync_result__: "{{ __llama_sync_output__.stdout | from_json }}"
- name: Ensure router preset file exists when not in dry-run mode
become: true
become_user: "{{ podman_user }}"
ansible.builtin.stat:
path: "{{ __llama_preset_file__ }}"
register: __llama_preset_stat__
when: not (llama_models_dry_run | default(false))
- name: Ensure router preset file exists when not in dry-run mode
ansible.builtin.assert:
that:
- __llama_preset_stat__.stat.exists
fail_msg: "llama router preset file was not created"
when: not (llama_models_dry_run | default(false))
- name: Remove temporary sudo access
become: true
ansible.builtin.file:
path: "/etc/sudoers.d/ansible_{{ podman_user }}_tmp"
state: absent
-28
View File
@@ -1,28 +0,0 @@
- name: Install nvidia-cdi-generator
become: true
ansible.builtin.package:
name:
- nvidia-container-toolkit
state: present
- name: Create systemd service file for nvidia-cdi-generator
become: true
ansible.builtin.copy:
src: services/nvidia-cdi-generator.service
dest: /etc/systemd/system/nvidia-cdi-generator.service
owner: root
group: root
mode: "0644"
- name: Reload systemd daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
name: nvidia-cdi-generator
- name: Enable nvidia-cdi-generator service
become: true
ansible.builtin.systemd:
name: nvidia-cdi-generator
enabled: true
state: started
-148
View File
@@ -1,148 +0,0 @@
- name: Check if required vars are set
ansible.builtin.fail:
msg: "Required variables are not set for Podman setup. Please check your playbook variables.)"
when:
- podman_user is not defined
- podman_group is not defined
- podman_user_home is not defined
- name: Set fact podman_binary (Debian family)
when: ansible_os_family == 'Debian'
ansible.builtin.set_fact:
podman_binary: /usr/bin/podman
- name: Set fact podman_binary (Red Hat family)
when: ansible_os_family == 'RedHat'
ansible.builtin.set_fact:
podman_binary: /usr/sbin/podman
- name: Install Podman and its dependencies
become: true
ansible.builtin.package:
name:
- podman
- acl
state: present
- name: Setup user
ansible.builtin.include_tasks:
file: podman/user.yml
- name: Enable sudo access to podman user (temporary)
become: true
ansible.builtin.lineinfile:
path: "/etc/sudoers.d/ansible_{{ podman_user }}_tmp"
create: true
mode: "0440"
line: "{{ ansible_facts['user_id'] }} ALL=({{ podman_user }}) NOPASSWD: ALL"
validate: "visudo -cf %s"
- name: Enable lingering for podman user
become: true
ansible.builtin.command: loginctl enable-linger {{ podman_user }}
register: linger_status
changed_when: >-
'created' in linger_status.stdout or
(linger_status.rc == 0 and not
('linger file already exists' in linger_status.stderr or
'linger file does not exist' in linger_status.stderr))
failed_when:
- linger_status.rc != 0 and not
('linger file already exists' in linger_status.stderr)
- name: Get current user's UID
ansible.builtin.command: id -u {{ podman_user }}
changed_when: false
register: uid
- name: Get current user's GID
ansible.builtin.command: id -g {{ podman_user }}
changed_when: false
register: gid
- name: Run tasks as podman user
become: true
become_user: "{{ podman_user }}"
block:
- name: Create a custom Podman network
when: podman_network_name is defined and podman_network_name != 'none'
containers.podman.podman_network:
name: "{{ podman_network_name }}"
state: present
- name: Copy build files to user home
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ podman_user_home }}/build/"
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0644"
loop: "{{ podman_build_files | default([]) }}"
when: podman_build_files is defined and podman_build_files | length > 0
- name: Build containers images
containers.podman.podman_image:
name: "{{ podman_user }}/{{ item.name }}"
path: "{{ podman_user_home }}/build"
build:
cache: false
force_rm: true
container_file: "{{ item.build.dockerfile }}"
extra_args: "--build-arg UID={{ uid.stdout }} --build-arg GID={{ gid.stdout }} --security-opt=label=disable {{ item.build.args | default('') }}"
tag: "latest"
state: present
loop: "{{ pods }}"
when: item.build is defined and item.build.dockerfile is defined
- name: Pull the requested images
containers.podman.podman_image:
name: "{{ item.repo.image }}"
tag: "{{ item.repo.tag | default('latest') }}"
state: present
loop: "{{ pods }}"
when: item.repo is defined and item.repo.image is defined
- name: Create pods
containers.podman.podman_container:
name: "{{ item.name }}"
image: "{{ item.repo.image | default(podman_user + '/' + item.name) }}"
state: "{{ item.state | default('started') }}"
device: "{{ item.device | default(omit) }}"
env: "{{ item.env | default(omit) }}"
network: "{{ podman_network_name | default(omit) }}"
ports: "{{ item.ports | default(omit) }}"
volumes: "{{ item.volumes | default(omit) }}"
command: "{{ item.command | default(omit) }}"
cmd_args:
- "--userns=keep-id"
- "--security-opt=label=disable"
loop: "{{ pods }}"
- name: Create systemd service file for pods
become: true
ansible.builtin.template:
src: podman.service.j2
dest: "/etc/systemd/system/podman-{{ item.name }}.service"
owner: root
group: root
mode: "0644"
loop: "{{ pods }}"
- name: Reload systemd daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
- name: Enable containers at boot
become: true
ansible.builtin.systemd:
name: "podman-{{ item.name }}"
enabled: "{{ item.systemd.enabled | default(true) }}"
state: "{{ item.systemd.state | default('started') }}"
loop: "{{ pods }}"
- name: Remove temporary sudo access
become: true
ansible.builtin.file:
path: "/etc/sudoers.d/ansible_{{ podman_user }}_tmp"
state: absent
-84
View File
@@ -1,84 +0,0 @@
- name: Check if required vars are set
ansible.builtin.fail:
msg: "Required variables are not set for Podman setup. Please check your playbook variables.)"
when:
- podman_user is not defined
- podman_group is not defined
- podman_user_home is not defined
- name: Create group
become: true
ansible.builtin.group:
name: "{{ podman_group }}"
system: true
- name: Create user
become: true
ansible.builtin.user:
name: "{{ podman_user }}"
groups: "{{ podman_extra_groups | default([]) }}"
shell: /bin/bash
home: "{{ podman_user_home }}"
group: "{{ podman_group }}"
- name: Disable password login for user
ansible.builtin.command: passwd -d "{{ podman_user }}"
become: true
changed_when: false
- name: Create user home
become: true
ansible.builtin.file:
path: "{{ podman_user_home }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0750"
- name: Create build folders
become: true
ansible.builtin.file:
path: "{{ podman_user_home }}/build"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0750"
- name: Create folders
become: true
ansible.builtin.file:
path: "{{ podman_user_home }}/{{ item }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_group }}"
mode: "0750"
loop: "{{ podman_user_folders }}"
- name: "Add SSH Authorized key"
become: true
ansible.posix.authorized_key:
user: "{{ podman_user }}"
key: "{{ podman_user_pubkey }}"
state: "{{ 'present' if podman_user_pubkey is defined else 'absent' }}"
when: podman_user_pubkey is defined and podman_user_pubkey != ""
- name: SELinux tasks (RedHat only)
when: ansible_os_family == 'RedHat'
become: true
block:
- name: Persistently set SELinux context
community.general.sefcontext:
target: "{{ podman_user_home }}(/.*)?"
setype: user_home_dir_t
state: present
- name: Persistently set SELinux context (ssh authorized keys)
community.general.sefcontext:
target: "{{ podman_user_home }}/.ssh/authorized_keys"
setype: ssh_home_t
state: present
when: podman_user_pubkey is defined and podman_user_pubkey != ""
- name: Apply SELinux context
ansible.builtin.command: restorecon -Rv {{ podman_user_home }}
changed_when: false
-186
View File
@@ -1,186 +0,0 @@
- name: Create limited GameUser
ansible.windows.win_user:
name: "{{ game_user }}"
password: "{{ game_password }}"
groups: "{{ game_default_groups }}"
account_disabled: false
password_never_expires: true
user_cannot_change_password: true
state: present
- name: Configure auto-login for GameUser
ansible.windows.win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
name: "{{ item.name }}"
data: "{{ item.data }}"
type: string
loop:
- { name: "AutoAdminLogon", data: "1" }
- { name: "DefaultUserName", data: "{{ game_user }}" }
- { name: "DefaultPassword", data: "{{ game_password }}" }
- { name: "DefaultDomainName", data: "{{ ansible_hostname }}" }
- name: Create launcher script directory
ansible.windows.win_file:
path: C:\GameShell
state: directory
- name: Deploy launcher.bat
ansible.windows.win_copy:
dest: C:\GameShell\launcher.bat
content: "{{ lookup('file', 'scripts/launcher.bat') }}"
- name: Set High Performance Power Plan
ansible.windows.win_command: powercfg -setactive SCHEME_MIN
- name: Disable auto suspend (sleep) for plugged in
ansible.windows.win_command: powercfg -change -standby-timeout-ac 0
- name: Disable auto suspend (sleep) for battery
ansible.windows.win_command: powercfg -change -standby-timeout-dc 0
- name: Disable Windows Defender Real-Time Protection
ansible.windows.win_shell: Set-MpPreference -DisableRealtimeMonitoring $true
- name: Remove unnecessary startup items (HKLM)
ansible.windows.win_regedit:
path: HKLM:\Software\Microsoft\Windows\CurrentVersion\Run
name: SecurityHealth
state: absent
- name: Remove startup folder shortcuts (user)
ansible.windows.win_file:
path: "{{ item }}"
state: absent
with_fileglob:
- "{{ ansible_env.APPDATA }}\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\*.lnk"
- name: Remove startup folder shortcuts (all users)
ansible.windows.win_file:
path: "{{ item }}"
state: absent
with_fileglob:
- "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\*.lnk"
- name: Disable Xbox Game Bar and DVR
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\GameDVR
name: AppCaptureEnabled
data: 0
type: dword
state: present
- name: Disable GameDVR_Enabled
ansible.windows.win_regedit:
path: HKCU:\System\GameConfigStore
name: GameDVR_Enabled
data: 0
type: dword
state: present
- name: Disable GameDVR_FSEBehaviorMode
ansible.windows.win_regedit:
path: HKCU:\System\GameConfigStore
name: GameDVR_FSEBehaviorMode
data: 2
type: dword
state: present
- name: Disable GameDVR_HonorUserFSEBehaviorMode
ansible.windows.win_regedit:
path: HKCU:\System\GameConfigStore
name: GameDVR_HonorUserFSEBehaviorMode
data: 1
type: dword
state: present
- name: Disable Windows notifications
ansible.windows.win_regedit:
path: HKCU:\Software\Microsoft\Windows\CurrentVersion\PushNotifications
name: ToastEnabled
data: 0
type: dword
state: present
- name: Disable background services
vars:
services_to_disable:
- WSearch
- SysMain
- DiagTrack
- XblAuthManager
- XblGameSave
- XboxGipSvc
- Fax
- MapsBroker
- RetailDemo
- RemoteRegistry
- WMPNetworkSvc
- edgeupdate
- WpnService
- BITS
- WerSvc
- Spooler
- SCardSvr
ansible.windows.win_service:
name: "{{ item }}"
start_mode: disabled
state: stopped
loop: "{{ services_to_disable }}"
- name: Get all services
ansible.windows.win_service_info:
register: win_services
- name: Disable scheduled telemetry tasks
vars:
scheduled_tasks_to_disable:
- "\\Microsoft\\Windows\\Application Experience\\ProgramDataUpdater"
- "\\Microsoft\\Windows\\Customer Experience Improvement Program\\Consolidator"
- "\\Microsoft\\Windows\\Customer Experience Improvement Program\\KernelCeipTask"
- "\\Microsoft\\Windows\\Feedback\\Siuf\\DmClient"
- "\\Microsoft\\Windows\\Windows Error Reporting\\QueueReporting"
ansible.windows.win_shell: |
schtasks /Change /TN "{{ item }}" /Disable
loop: "{{ scheduled_tasks_to_disable }}"
register: telemetry_task_result
failed_when: "{{ telemetry_task_result.rc != 0 and ('ERROR: The system cannot find the file specified.' not in telemetry_task_result.stderr) }}"
- name: Disable Windows Search feature
ansible.windows.win_shell: DISM /Online /Disable-Feature /FeatureName:SearchEngine-Client-Package /NoRestart
register: disable_search_result
failed_when: >
disable_search_result.rc != 0 and
('The specified package is not valid Windows package' not in disable_search_result.stderr) and
('is already disabled' not in disable_search_result.stdout)
- name: Disable Windows Media Player feature
ansible.windows.win_shell: DISM /Online /Disable-Feature /FeatureName:WindowsMediaPlayer /NoRestart
register: disable_wmp_result
failed_when: >
disable_wmp_result.rc != 0 and
('The specified package is not valid Windows package' not in disable_wmp_result.stderr) and
('is already disabled' not in disable_wmp_result.stdout)
- name: Disable Print to PDF
ansible.windows.win_shell: DISM /Online /Disable-Feature /FeatureName:Printing-PrintToPDFServices-Features /NoRestart
register: disable_print_pdf_result
failed_when: >
disable_print_pdf_result.rc != 0 and
('The specified package is not valid Windows package' not in disable_print_pdf_result.stderr) and
('is already disabled' not in disable_print_pdf_result.stdout)
- name: Reminder - NVIDIA Control Panel Tweaks (manual)
ansible.builtin.debug:
msg: |
Please manually set the following in NVIDIA Control Panel:
- Power Management: Prefer Maximum Performance
- Low Latency Mode: Ultra
- Vertical Sync: Off or Fast Sync (depending on monitor)
# - name: Set custom shell to launcher.bat
# ansible.windows.win_regedit:
# path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
# name: Shell
# data: C:\GameShell\launcher.bat
# type: string
-199
View File
@@ -1,199 +0,0 @@
---
# Install Prometheus + Windows Exporter on Windows and open firewall for Grafana
- name: Define Prometheus Versions and install dir
ansible.builtin.set_fact:
prometheus_version: "{{ prometheus_version | default('3.6.0') }}"
prometheus_install_dir: "{{ prometheus_install_dir | default('C:\\Prometheus') }}"
prometheus_listen_address: "{{ prometheus_listen_address | default('0.0.0.0:9090') }}"
nssm_dir: "{{ nssm_dir | default('C:\\nssm') }}"
nssm_version: "{{ nssm_version | default('2.24') }}"
windows_exporter_version: "{{ windows_exporter_version | default('0.31.3') }}"
windows_exporter_collectors: "{{ windows_exporter_collectors | default(['cache', 'cpu', 'cpu_info', 'diskdrive', 'memory', 'net', 'os', 'process', 'service', 'tcp', 'time', 'update']) }}"
- name: Define Windows Exporter defaults
ansible.builtin.set_fact:
__nssm_url: "https://nssm.cc/release/nssm-{{ nssm_version }}.zip"
__prometheus_zip_url: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.windows-amd64.zip"
__prometheus_zip_path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\prometheus-{{ prometheus_version }}.zip"
__prometheus_home: "{{ prometheus_install_dir }}\\prometheus-{{ prometheus_version }}.windows-amd64"
__windows_exporter_msi_url: "https://github.com/prometheus-community/windows_exporter/releases/download/v{{ windows_exporter_version }}/windows_exporter-{{ windows_exporter_version }}-amd64.msi"
__windows_exporter_msi_path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\windows_exporter-{{ windows_exporter_version }}-amd64.msi"
__nssm_zip_path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\nssm.zip"
__nssm_exe: "{{ nssm_dir }}\\nssm.exe"
- name: Create Prometheus installation directories
ansible.windows.win_file:
path: "{{ item }}"
state: directory
loop:
- "{{ prometheus_install_dir }}"
- "{{ prometheus_install_dir }}\\data"
- "{{ nssm_dir }}"
- name: Download required files (Prometheus, NSSM, Windows Exporter)
ansible.windows.win_get_url:
url: "{{ item.url }}"
dest: "{{ item.dest }}"
force: false
loop:
- url: "{{ __prometheus_zip_url }}"
dest: "{{ __prometheus_zip_path }}"
- url: "{{ __nssm_url }}"
dest: "{{ __nssm_zip_path }}"
- url: "{{ __windows_exporter_msi_url }}"
dest: "{{ __windows_exporter_msi_path }}"
- name: Extract downloaded archives
community.windows.win_unzip:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
delete_archive: false
creates: "{{ item.creates | default(omit) }}"
loop:
- src: "{{ __prometheus_zip_path }}"
dest: "{{ prometheus_install_dir }}"
creates: "{{ __prometheus_home }}\\prometheus.exe"
- src: "{{ __nssm_zip_path }}"
dest: "{{ nssm_dir }}"
- name: Verify Prometheus binary is present
ansible.windows.win_stat:
path: "{{ __prometheus_home }}\\prometheus.exe"
register: prometheus_bin
- name: Fail if Prometheus binary is missing
ansible.builtin.fail:
msg: >-
Prometheus binary not found at {{ __prometheus_home }}\prometheus.exe.
Check download/unzip and prometheus_version.
when: not prometheus_bin.stat.exists
- name: Deploy Prometheus configuration
ansible.windows.win_copy:
dest: "{{ prometheus_install_dir }}\\prometheus.yml"
content: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'windows'
static_configs:
- targets: ['localhost:9182']
- name: Check if promtool exists
ansible.windows.win_stat:
path: "{{ __prometheus_home }}\\promtool.exe"
register: promtool_bin
- name: Validate Prometheus config with promtool
ansible.windows.win_command: >-
"{{ __prometheus_home }}\promtool.exe"
check config
"{{ prometheus_install_dir }}\prometheus.yml"
register: promtool_check
changed_when: false
failed_when: promtool_check.rc != 0
when: promtool_bin.stat.exists
- name: Copy NSSM executable (win64)
ansible.windows.win_copy:
src: "{{ nssm_dir }}\\nssm-2.24\\win64\\nssm.exe"
dest: "{{ __nssm_exe }}"
remote_src: true
- name: Remove existing Prometheus service if present
ansible.windows.win_service:
name: Prometheus
state: absent
ignore_errors: true
- name: Install Prometheus service via NSSM
ansible.windows.win_command: >-
"{{ __nssm_exe }}"
install Prometheus
"{{ __prometheus_home }}\prometheus.exe"
--config.file={{ prometheus_install_dir }}\prometheus.yml
--storage.tsdb.path={{ prometheus_install_dir }}\data
--web.listen-address={{ prometheus_listen_address }}
--web.console.templates={{ __prometheus_home }}\consoles
--web.console.libraries={{ __prometheus_home }}\console_libraries
args:
chdir: "{{ __prometheus_home }}"
register: nssm_install
changed_when: nssm_install.rc == 0
- name: Set Prometheus AppDirectory via NSSM
ansible.windows.win_command: >-
"{{ __nssm_exe }}" set Prometheus AppDirectory "{{ __prometheus_home }}"
- name: Configure Prometheus stdout log via NSSM
ansible.windows.win_command: >-
"{{ __nssm_exe }}" set Prometheus AppStdout
"{{ prometheus_install_dir }}\prometheus-service.out.log"
- name: Configure Prometheus stderr log via NSSM
ansible.windows.win_command: >-
"{{ __nssm_exe }}" set Prometheus AppStderr
"{{ prometheus_install_dir }}\prometheus-service.err.log"
- name: Set Prometheus Start mode to AUTO via NSSM
ansible.windows.win_command: >-
"{{ __nssm_exe }}" set Prometheus Start SERVICE_AUTO_START
- name: Ensure Prometheus service is running (skip in check mode)
ansible.windows.win_service:
name: Prometheus
start_mode: auto
state: started
when: not ansible_check_mode | default(false)
- name: Wait for Prometheus to listen on 9090 (skip in check mode)
ansible.windows.win_wait_for:
port: 9090
delay: 0
timeout: 30
when: not ansible_check_mode | default(false)
- name: Install Windows Exporter
ansible.windows.win_package:
path: "{{ __windows_exporter_msi_path }}"
arguments: 'ENABLED_COLLECTORS={{ windows_exporter_collectors | join(",") }}'
state: present
- name: Ensure windows_exporter service is running
ansible.windows.win_service:
name: windows_exporter
start_mode: auto
state: started
- name: Set firewall remote IPs when string provided
ansible.builtin.set_fact:
grafana_remoteip: "{{ grafana_prometheus_sources }}"
when:
- grafana_prometheus_sources is defined
- grafana_prometheus_sources is string
- name: Set firewall remote IPs when list provided
ansible.builtin.set_fact:
grafana_remoteip: "{{ grafana_prometheus_sources | join(',') }}"
when:
- grafana_prometheus_sources is defined
- not (grafana_prometheus_sources is string)
- name: Allow inbound Prometheus (9090) from Grafana
community.windows.win_firewall_rule:
name: Prometheus 9090 (Grafana)
localport: 9090
protocol: tcp
direction: in
action: allow
enabled: true
# Provide one or more IPs/CIDRs in grafana_prometheus_sources (e.g. ['203.0.113.10'])
remoteip: "{{ grafana_remoteip | default(omit) }}"
state: present
-373
View File
@@ -1,373 +0,0 @@
# Expects variable `ssh_users` as list of objects:
# ssh_users:
# - name: alice
# keys:
# - "ssh-ed25519 AAAA..."
# - "ssh-rsa AAAA..."
# - name: bob
# keys:
# - "ssh-rsa AAAA..."
- name: Setting defaults for Windows OpenSSH Server installation
ansible.builtin.set_fact:
__ssh_default_ciphers__:
- chacha20-poly1305@openssh.com
- aes256-gcm@openssh.com
- aes256-ctr
__ssh_default_hostkey_algorithms__:
- ssh-ed25519-cert-v01@openssh.com
- ssh-rsa-cert-v01@openssh.com
- ssh-ed25519
- ssh-rsa
- ecdsa-sha2-nistp521-cert-v01@openssh.com
- ecdsa-sha2-nistp384-cert-v01@openssh.com
- ecdsa-sha2-nistp256-cert-v01@openssh.com
- ecdsa-sha2-nistp521
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp256
- sk-ecdsa-sha2-nistp256@openssh.com
__ssh_default_kex_algorithms__:
- mlkem768x25519-sha256
- sntrup761x25519-sha512@openssh.com
- curve25519-sha256
- curve25519-sha256@libssh.org
- ecdh-sha2-nistp521
- ecdh-sha2-nistp384
- ecdh-sha2-nistp256
- diffie-hellman-group-exchange-sha256
__ssh_default_macs__:
- hmac-sha2-512-etm@openssh.com
- hmac-sha2-256-etm@openssh.com
- hmac-sha2-512
- hmac-sha2-256
__ssh_default_auth_pubkey_accepted_algorithms__:
- ecdsa-sha2-nistp521
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp256
- ssh-ed25519
- rsa-sha2-512
- rsa-sha2-256
- name: Override defaults with any provided variables
ansible.builtin.set_fact:
__ssh_users__: "{{ ssh_users | default([]) }}"
__ssh_ciphers__: "{{ ssh_ciphers | default(__ssh_default_ciphers__) }}"
__ssh_hostkey_algorithms__: "{{ ssh_hostkey_algorithms | default(__ssh_default_hostkey_algorithms__) }}"
__ssh_kex_algorithms__: "{{ ssh_kex_algorithms | default(__ssh_default_kex_algorithms__) }}"
__ssh_macs__: "{{ ssh_macs | default(__ssh_default_macs__) }}"
__ssh_max_auth_tries__: "{{ ssh_max_auth_tries | default(10) }}"
__ssh_max_sessions__: "{{ ssh_max_sessions | default(3) }}"
__ssh_pubkey_accepted_algorithms__: "{{ ssh_pubkey_accepted_algorithms | default(__ssh_default_auth_pubkey_accepted_algorithms__) }}"
- name: Get OpenSSH Server capability state
ansible.windows.win_shell: |
$cap = Get-WindowsCapability -Online | Where-Object { $_.Name -like 'OpenSSH.Server*' } | Select-Object -First 1
if ($cap) {
$cap.State.ToString().Trim()
} else {
'NotPresent'
}
args:
executable: powershell.exe
register: openssh_capability_state
changed_when: false
- name: Install OpenSSH Server capability when not installed
ansible.windows.win_shell: |
$cap = Get-WindowsCapability -Online | Where-Object { $_.Name -like 'OpenSSH.Server*' } | Select-Object -First 1
if (-not $cap) {
throw 'OpenSSH.Server capability not found on this host.'
}
Add-WindowsCapability -Online -Name $cap.Name
args:
executable: powershell.exe
register: openssh_install
when: openssh_capability_state.stdout | trim != 'Installed'
- name: Check whether OpenSSH host keys already exist
ansible.windows.win_shell: |
$keys = Get-ChildItem -Path 'C:\ProgramData\ssh' -Filter 'ssh_host_*_key' -File -ErrorAction SilentlyContinue
if ($keys -and $keys.Count -gt 0) {
Write-Output 'present'
} else {
Write-Output 'missing'
}
args:
executable: powershell.exe
register: ssh_hostkeys_state
changed_when: false
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Generate OpenSSH host keys when missing (ssh-keygen -A)
ansible.windows.win_shell: |
$exe = 'C:\Windows\System32\OpenSSH\ssh-keygen.exe'
if (Test-Path $exe) {
& $exe -A 2>&1
Write-Output 'hostkeys-generated'
} else {
Write-Output 'ssh-keygen-not-found'
exit 2
}
register: ssh_keygen
changed_when: "'hostkeys-generated' in ssh_keygen.stdout"
when: ssh_hostkeys_state.stdout | trim == 'missing'
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Ensure OpenSSH private host keys are owned by SYSTEM
ansible.windows.win_shell: |
$changed = $false
$keys = Get-ChildItem -Path 'C:\ProgramData\ssh' -Filter 'ssh_host_*_key' -File -ErrorAction SilentlyContinue
if ($keys -and $keys.Count -gt 0) {
foreach ($k in $keys) {
$before = (Get-Acl -Path $k.FullName).Owner
icacls.exe $k.FullName /setowner "*S-1-5-18" /C | Out-Null
$after = (Get-Acl -Path $k.FullName).Owner
if ($before -ne $after) {
$changed = $true
}
}
if ($changed) {
Write-Output 'hostkey-owner-changed'
} else {
Write-Output 'hostkey-owner-unchanged'
}
} else {
Write-Output 'no-keys'
}
register: ssh_hostkey_owner
changed_when: "'hostkey-owner-changed' in ssh_hostkey_owner.stdout"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Restrict private host key file permissions (icacls) and remove provision user access
ansible.windows.win_shell: |
$changed = $false
$removePrincipal = "{{ ansible_user | default('') }}"
$keys = Get-ChildItem -Path 'C:\ProgramData\ssh' -Filter 'ssh_host_*_key' -File -ErrorAction SilentlyContinue
if ($keys -and $keys.Count -gt 0) {
foreach ($k in $keys) {
$before = (Get-Acl -Path $k.FullName).Sddl
icacls.exe $k.FullName /inheritance:r
icacls.exe $k.FullName /grant:r "S-1-5-18:(F)"
if ($removePrincipal -and $removePrincipal -notmatch '^(?i:NT AUTHORITY\\SYSTEM|SYSTEM)$') {
icacls.exe $k.FullName /remove "$removePrincipal" 2>$null
}
$after = (Get-Acl -Path $k.FullName).Sddl
if ($before -ne $after) {
$changed = $true
}
}
if ($changed) {
Write-Output 'restricted-changed'
} else {
Write-Output 'restricted-unchanged'
}
} else {
Write-Output 'no-keys'
}
args:
executable: powershell.exe
register: restrict_hostkey_perms
changed_when: "'restricted-changed' in restrict_hostkey_perms.stdout"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Restrict C:\ProgramData\ssh folder write permissions
ansible.windows.win_shell: |
$p = 'C:\ProgramData\ssh'
if (Test-Path $p) {
$before = (Get-Acl -Path $p).Sddl
icacls.exe $p /inheritance:r | Out-Null
icacls.exe $p /grant:r "S-1-5-18:(OI)(CI)(F)" "S-1-5-32-544:(OI)(CI)(F)" | Out-Null
icacls.exe $p /remove "S-1-5-32-545" 2>$null
$after = (Get-Acl -Path $p).Sddl
if ($before -ne $after) {
Write-Output 'ssh-dir-acl-changed'
} else {
Write-Output 'ssh-dir-acl-unchanged'
}
} else {
Write-Output 'no-dir'
}
args:
executable: powershell.exe
register: ssh_dir_acl
changed_when: "'ssh-dir-acl-changed' in ssh_dir_acl.stdout"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Ensure sshd service is enabled and running
ansible.windows.win_service:
name: sshd
start_mode: auto
state: started
- name: Ensure firewall allows SSH (TCP/22)
community.windows.win_firewall_rule:
name: "OpenSSH Server (sshd)"
enable: true
direction: in
action: allow
localport: 22
protocol: TCP
profile: "Domain,Private"
- name: Ensure SSH banner file is present
ansible.windows.win_copy:
src: "windows/issue.net"
dest: 'C:\ProgramData\ssh\issue.net'
force: true
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Detect available PowerShell for ssh subsystem
ansible.windows.win_shell: |
if (Test-Path 'C:\Program Files\PowerShell\7\pwsh.exe') { Write-Output 'C:\Program Files\PowerShell\7\pwsh.exe' }
elseif (Test-Path 'C:\Program Files\PowerShell\6\pwsh.exe') { Write-Output 'C:\Program Files\PowerShell\6\pwsh.exe' }
elseif (Test-Path 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe') { Write-Output 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' }
else { Write-Output '' }
register: ssh_subsystem_path
changed_when: false
- name: Set ssh subsystem executable path fact
ansible.builtin.set_fact:
__ssh_subsystem_path__: "{{ ssh_subsystem_path.stdout | trim }}"
- name: Detect supported SSH key exchange algorithms
ansible.windows.win_shell: |
$ssh = 'C:\Windows\System32\OpenSSH\ssh.exe'
if (Test-Path $ssh) {
& $ssh -Q kex
} else {
Write-Output ''
}
args:
executable: powershell.exe
register: ssh_supported_kex
changed_when: false
- name: Render sshd_config from template
ansible.windows.win_template:
src: windows_sshd_config.j2
dest: 'C:\ProgramData\ssh\sshd_config'
register: sshd_config_changed
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Restart sshd if config changed # noqa no-handler
ansible.windows.win_service:
name: sshd
state: restarted
when: sshd_config_changed.changed
- name: Ensure users for SSH keys exist (no-op if already present)
ansible.windows.win_user:
name: "{{ item.name }}"
state: present
loop: "{{ __ssh_users__ | default([]) }}"
loop_control:
label: "{{ item.name }}"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Ensure user .ssh directory exists
ansible.windows.win_file:
path: "C:\\Users\\{{ item.name }}\\.ssh"
state: directory
loop: "{{ __ssh_users__ | default([]) }}"
loop_control:
label: "{{ item.name }}"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Deploy authorized_keys for each user
ansible.windows.win_copy:
dest: "C:\\Users\\{{ item.name }}\\.ssh\\authorized_keys"
content: "{{ (item['keys'] | default([])) | join('\r\n') }}\r\n"
force: true
loop: "{{ __ssh_users__ | default([]) }}"
loop_control:
label: "{{ item.name }}"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Set ACL on .ssh directory to allow only the user and Administradores (directory)
ansible.windows.win_acl:
path: "C:\\Users\\{{ item.name }}\\.ssh"
user: "{{ item.name }}"
rights: FullControl
type: allow
inheritance: false
propagate: false
state: present
loop: "{{ __ssh_users__ | default([]) }}"
loop_control:
label: "{{ item.name }}"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Set ACL on authorized_keys file to allow only the user and Administradores (file)
ansible.windows.win_acl:
path: "C:\\Users\\{{ item.name }}\\.ssh\\authorized_keys"
user: "{{ item.name }}"
rights: FullControl
type: allow
inheritance: false
propagate: false
state: present
loop: "{{ __ssh_users__ | default([]) }}"
loop_control:
label: "{{ item.name }}"
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Create administrators_authorized_keys from admin users' keys (if any)
ansible.windows.win_copy:
dest: 'C:\ProgramData\ssh\administrators_authorized_keys'
content: >-
{{ (__ssh_users__ | default([]) | selectattr('admin', 'equalto', true)
| map(attribute='keys') | list | flatten | default([])) | join('\r\n') }}\r\n
force: true
when: (__ssh_users__ | default([]) | selectattr('admin','equalto',true) | list) | length > 0
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
- name: Restrict administrators_authorized_keys permissions
ansible.windows.win_shell: |
$path = 'C:\ProgramData\ssh\administrators_authorized_keys'
if (Test-Path $path) {
$before = (Get-Acl -Path $path).Sddl
icacls.exe $path /inheritance:r | Out-Null
icacls.exe $path /grant:r "S-1-5-18:(F)" "S-1-5-32-544:(F)" | Out-Null
$after = (Get-Acl -Path $path).Sddl
if ($before -ne $after) {
Write-Output 'admin-auth-keys-acl-changed'
} else {
Write-Output 'admin-auth-keys-acl-unchanged'
}
} else {
Write-Output 'no-admin-auth-keys'
}
args:
executable: powershell.exe
register: restrict_admin_auth_perms
changed_when: "'admin-auth-keys-acl-changed' in restrict_admin_auth_perms.stdout"
when: (__ssh_users__ | default([]) | selectattr('admin','equalto',true) | list) | length > 0
become: true
become_method: ansible.builtin.runas
become_user: "NT AUTHORITY\\SYSTEM"
-58
View File
@@ -1,58 +0,0 @@
- name: Install ethtool
become: true
ansible.builtin.package:
name: ethtool
state: present
- name: Enable WOL on the network interface
become: true
ansible.builtin.command: ethtool -s {{ network_interface }} wol g
changed_when: false
- name: Create systemd-networkd WOL config
become: true
ansible.builtin.copy:
dest: "/etc/systemd/network/10-wol.network"
content: |
[Match]
Name={{ network_interface }}
[Link]
WakeOnLan=magic
owner: root
group: root
mode: "0644"
- name: Create systemd WOL config
become: true
ansible.builtin.copy:
dest: "/etc/systemd/system/wol.service"
content: |
[Unit]
Description=Enable Wake-on-LAN
After=network-online.target
[Service]
Type=oneshot
ExecStart=/sbin/ethtool --change {{ network_interface }} wol g
[Install]
WantedBy=network-online.target
owner: root
group: root
mode: "0644"
- name: Enable and restart systemd-networkd
become: true
ansible.builtin.systemd:
name: wol.service
state: started
enabled: true
- name: Confirm WOL setting applied
become: true
ansible.builtin.command: ethtool {{ network_interface }}
register: wol_status
changed_when: false
- name: Show WOL setting
ansible.builtin.debug:
var: wol_status.stdout_lines