Ansible - Edit a systemd service file - ansible

The systemd module: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/systemd_module.html
I'm looking for a way to add a Condition to the service file.
For instance:
ConditionPathIsMountPoint=/mnt/myreplication/path/
This would be useful for docker installations, ensuring docker doesn't start containers before a mount they need is actually available.
Sadly, it looks like Ansible doesn't support adding this right now. Am I correct there? Will I need to manually add it, or with lineinfile? Or is there an other way?
EDIT: This question appears to be getting views, so I'll add this:
https://askubuntu.com/questions/659267/how-do-i-override-or-configure-systemd-services
And this answer to another question of mine: https://askubuntu.com/a/1348117/1612
To quote it:
Don't edit files in /lib/systemd/ or /usr/share/systemd as they will get overwritten on updates.

Let me post a solution using ini_file that worked for me:
- name: Create a foo.service override directory
file:
owner: root
group: root
mode: 0755
path: /etc/systemd/system/foo.service.d
state: directory
- name: Set up foo.service override
ini_file:
dest: /etc/systemd/system/foo.service.d/bar_override.conf
owner: root
group: root
mode: 0644
section: Unit
option: ConditionPathIsMountPoint
value: /mnt/myreplication/path/
This avoids rewriting the original service file, but rather adds a dedicated override into a .d subdirectory.
Note that ini_file adds whitespace around = as in
[Unit]
ConditionPathIsMountPoint = /mnt/myreplication/path/
but this is fine, see systemd.syntax(7):
Each file is a plain text file divided into sections, with configuration entries in the style key=value. Whitespace immediately before or after the "=" is ignored.

Am I correct there?
Right, the systemd_module is not for manipulating service files.
Since I've had some similar questions in the past I like to share my approach.
You could either maintain your own service file template and deploy it
- name: "Make sure the systemd service file is correct"
template:
src: "{{ MYSERVICE }}.service.j2"
dest: "/etc/systemd/system/{{ MYSERVICE }}.service"
mode: 0755
tags: install,systemd
or add the necessary line via lineinfile_module
- name: "Make sure the entry in '{{ MYSERVICE }}.service' exists"
lineinfile:
path: "/etc/systemd/system/{{ MYSERVICE }}.service"
line: "ConditionPathIsMountPoint=/mnt/myreplication/path/"
state: present
tags: install,systemd
and reload and restart the service
- name: "Make sure the service is started and enabled via systemd"
systemd:
name: "{{ MYSERVICE }}"
state: started
enabled: yes
daemon_reload: yes
tags: install,systemd
whereby it might be good to use insertbefore or insertafter also.

EDIT: This question appears to be getting views, so I'll add this:
https://askubuntu.com/questions/659267/how-do-i-override-or-configure-systemd-services
And this answer to another question of mine: https://askubuntu.com/a/1348117/1612
To quote it:
Don't edit files in /lib/systemd/ or /usr/share/systemd as they will get overwritten on updates.

Related

How can I ensure correct permissions when using Ansible to deploy an application

I am having a really hard time understanding how to approach privilege escalation when deploying a python application using Ansible.
In my current setup:
The VM is provisioned by our sysadmins
My user (let's call it "John") is not root, but can become it via calling sudo.
No matter what I do via Ansible, it ends being either done via John or root (with become: true).
I would like to:
install system packages like python3, pip or supervisord
setup separate system user
download the code using the git repository
create virtualenv
install requirements in the virtualenv
setup supervisord
The list above is really easy when I do it manually - I log into the VM, I use sudo su - to become superuser to handle points 1-2, then su new-system-user, complete points 3-5 etc.
Some of the tasks are also really easy to do in Ansible when using become: true or using my SSH user, however I get many permission issues when using the newly created system user, and become_user: "{{ system_user }}" in the tasks 3-5 seem not to work as intended.
I would like to ask you - what is the optimal way to tackle this issue? My only workaround is to do everything in the context of my SSH user, and then copy&chown to the new system-user, but this seems like a hack, and I'm pretty sure I'm missing something when it comes to the correct privilege escalation.
Even if I can't answer
What is the optimal way to tackle this issue?
fully, steps 1-3 and 6 might be possible just in a way
---
- hosts: pyapp
become: true
tasks:
- name: Make sure packages are installed
yum:
name: supervisor
state: latest
- name: Create group in local system
group:
name: pyapp
gid: '1234'
- name: Configure local account in system
user:
name: "pyapp"
system: yes
createhome: yes
uid: '1234'
group: '1234'
shell: /sbin/nologin
comment: "My Python App"
state: present
- name: Download and unpack
unarchive:
src: "https://{{ DOWNLOAD_URL }}/myPythonApp.tar.gz"
dest: "/home/pyapp/"
remote_src: yes
owner: "pyapp"
group: "pyapp"
- name: Make sure log directory exists
file:
path: "/var/log/pyapp"
state: directory
owner: 'pyapp'
group: 'pyapp'
- name: Make sure supervisord config file is provided
copy:
src: "pyapp.ini"
dest: "/etc/supervisord.d/pyapp.ini"
- name: Make sure service becomes started and enabled
systemd:
name: supervisord
state: started
enabled: true
daemon_reload: true
- name: Make sure application is in started state
supervisorctl:
name: pyapp
state: started
with a Configuration File (pyapp.ini) like
[program:pyapp]
directory=/home/pyapp
command=/usr/bin/python /home/pyapp/myPythonApp.py
user=pyapp
process_name=%(program_name)s
autorestart=true
stderr_logfile=/var/log/pyapp/stderr.log
stdout_logfile=/var/log/pyapp/stdout.log
leaving other values in default.
I am using such approach and it just works. Step 3, the download should also be possible by using the git module.
Module Documentaion in order of occurence
Understanding privilege escalation: become
group – Add or remove groups
user – Manage user accounts
unarchive – Unpacks an archive after (optionally) copying it from the local machine
file – Manage files and file properties
copy – Copy files to remote locations
systemd – Manage systemd units
supervisorctl – Manage the state of a program or group of programs running via supervisord
Further Readings
You may than have a look into Manages Python library dependencies and the Examples. As well Installing a Distribution Package and Running Supervisor.

How do I configure remote host journal.conf file with ansible?

I am trying to configure raspberry pi journalctl using ansible.
I tried using some ansible-galaxy roles which seem too complicated and did not deliver in the end.
I am just trying to configure the /etc/systemd/journald.conf file.
Can I do it with ansible.builtin.systemd or any other suggestions?
You only need a playbook and a template file.
myproject/changejournald.yml # your playbook
myproject/journald.conf.j2 # a jinja2 template, the 'journald.conf as you want it'
in changejournald.yml
---
- name: upload new template
template:
src: 'journald.conf.j2'
dest: '/etc/systemd/journald.conf'
become: true #<-- unless you are connecting as root
- name: reload systemd-journald
systemd:
name: systemd-journald
state: restart
become: true
Something like that should work?
There are also other modules like lineinfile or blockinfile that might be more useful depending on how you intend to configure it.
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html
https://unix.stackexchange.com/questions/253203/how-to-tell-journald-to-re-read-its-configuration

ansible link from directory to directory

I have a playbook that creates a directory, creates content on index.html, and a link from /web_hosting to /var/www/html.
The directory is called /web_hosting
the content is /web_hosting/index.html
I do not want to change the httpd.conf default web directory to /web_hosting I just want to use a link.
After running the play when I curl the server I'm not seeing the content from the index.html file.
Can someone help me with my play?
name: setup webserver and link to folder
hosts: prod
tasks:
name: create dir
file:
path: /web_hosting
state: directory
setype: httpd_sys_content_t
mode: 0775
name: install
yum:
name: httpd
state: present
name: configure service
service:
name: httpd
state: started
enabled: true
name: create content on index.html
copy:
dest: /web_hosting/index.html
content: "hello from {{ansible_hostname}}"
name: create link
file:
src: /web_hosting
dest: /var/www/html
state: link
This doesn't sound like an Ansible problem if it is creating the files and not erroring out.
If you manually create a file in /var/www/html/ called "index2.html", can you use curl to see it? If not, then it's definitely NOT an Ansible problem.
If that test works, then look for differences in ownership, SELinux permissions, etc. Then use Ansible to set those properly on your "index.html".
I suspect you might need to enable a "follow links" setting in your webserver configuration. But again, that's not an Ansible issue either - though Ansible could update the configuration file once you figure out what setting(s) to apply.

Cleaner way of choosing between start and restart

I have ansible configuration for deploying locally built daemons to a series of target machines, these daemons have associated systemd service files to control them.
What I want to happen is:
If daemon or unit file is changed then restart service
If daemon is unchanged then just start service (which may count as 'unchanged', because it's probably already running)
I'm doing this in a few places so I have a commonly repeating pattern that looks like:
- name: Populate the daemon
copy:
src: "local_build/mydaemon"
dest: "/usr/bin/mydaemon"
mode: 0775
register: daemon_bin
- name: Populate the service
template:
src: "Daemon.service"
dest: "/etc/systemd/system/mydaemon.service"
register: daemon_service
- name: Enable and restart
systemd:
state: restarted
daemon_reload: yes
enabled: yes
name: "mydaemon.service"
when: (daemon_bin.changed or daemon_service.changed)
- name: Enable and start
systemd:
state: started
enabled: yes
name: "mydaemon.service"
when: not (daemon_bin.changed or daemon_service.changed)
Is there a cleaner way to achieve this? It feels like it might be a common problem. Or is my approach somehow wrong?
Yes, you can use notify and handlers.

How to get facts at each command in Ansible

Ansible get facts only at start. But i need check facts at each commands.
For example:
I need create a directory, after that i need put file to this directory. But ansible get fact 'dir doesn't exist' at start, create dir and at next step fact still FALSE and ansible skip this step =( And do this step only after second run.
I'll try setup after all steps to gathering facts again but it doesn't work.
I do it like this:
- stat: path=/etc/zabbix/scripts/rabbitmq
register: rmqscriptdir
- name: Create scripts dir if not exist
when: rmqscriptdir.stat.exists == False
shell: mkdir /etc/zabbix/scripts/rabbitmq
- name: Gathering facts again
setup:
- name: Set owner and permissions to rabbitmq directory
when: rmqscriptdir.stat.exists == True
file: path=/etc/zabbix/scripts/rabbitmq owner=zabbix group=root mode=0750
- stat: path=/etc/zabbix/scripts/rabbitmq/api.py
register: rmqscript_api
- name: Create api.py if not exist
when: rmqscript_api.stat.exists == False and rmqscriptdir.stat.exists == True
shell: cd /etc/zabbix/scripts/rabbitmq; wget https://raw.githubusercontent.com/jasonmcintosh/rabbitmq-zabbix/master/scripts/rabbitmq/api.py
- name: Gathering facts again
setup:
- name: Set owner and permissions to api.py
when: rmqscript_api.stat.exists == True
file: path=/etc/zabbix/scripts/rabbitmq/api.py owner=zabbix group=root mode=0755
I think you misunderstand what the setup module does. By registering a value it does not become a fact that will be reloaded by the setup module when run again. Your registered value stays the same. If you want to check again if a path exists you do not need to re-run the setup module, but the stats module and again register its output.
But anyway, the idea of Ansible is actually to not manually check if every task should be executed or not. That is something Ansible takes care for you, Ansible in general is indepotent, meaning it will have the same result no matter how many times you run the play.
Here is a cleaned up version, which creates a folder and downloads the file. If the folder already exists, the 1st task will do nothing. If the file api.py already exists, the 2nd task will do nothing.
- name: Create scripts dir if not exist
file:
path: /etc/zabbix/scripts/rabbitmq
state: directory
owner: zabbix
group: root
mode: 0750
- name: Create api.py if not exist
get_url:
url: https://raw.githubusercontent.com/jasonmcintosh/rabbitmq-zabbix/master/scripts/rabbitmq/api.py
dest: /etc/zabbix/scripts/rabbitmq/api.py
owner: zabbix
group: root
mode: 0755
PS: If you want to see which values are reloaded by the setup module, you can register its output and show it in a debug task, like so:
- setup:
register: all_server_facts
- debug:
var: all_server_facts
This only contains server facts, info about cpu, hard drives, network etc. Also see this answer for an example output.

Resources