Ansible: Stop and Disable services if they are present - ansible

I'm trying to stop and disable a list of services only if they are installed. I'm calling the service_facts module to generate a list of running services and using filter "union"
- name: Include variables for Amazon Linux.
include_vars: ../vars/test.yml
- name: populate service facts
service_facts:
- name: start the service if it's enabled
service:
name: "{{ item }}"
enabled: no
state: stopped
loop: "{{ stop_services |union(services) }}"
when: stop_services is defined
I'm getting an error Unexpected templating type error occurred on ({{ stop_services |union(services) }}): can only concatenate list (not \"dict\") to list"
Is there any other way to stop and disable a list of services only if they are installed.

Your problem is described accurately by the error message: stop_services is a list, and services is a dictionary. You can't just squash the two together. You will need to build a list of service names and compare your list of services to stop against that.
For example:
- hosts: localhost
gather_facts: false
vars:
stop_services:
- sshd.service
- avahi-daemon.service
tasks:
- service_facts:
- name: stop and disable a service
service:
name: "{{ item }}"
state: stopped
enabled: false
when: "item in service_names"
loop: "{{ stop_services }}"
vars:
service_names: "{{ services|dict2items|map(attribute='value.name')|list }}"

Related

Stop/Start Windows services only when state not in manual or disabled

I have an Ansible playbook to start or stop services and I needed to add a condition to pick up services that are neither in Manual nor Disabled State. Appreciate your help !!!
My code looks like this.
---
- name: windows start services
hosts: all
tasks:
- name: Include primary app services
include_vars:
file: inventory/vars/servicestart.yml
- name: Start task
win_service:
name: "{{ item }}"
state: started
loop: "{{ services }}"
when: inventory_hostname in groups['primaryappservers']

Error handling for updating IOS via Ansible

beginner to using Ansible. More of a network engineer, less of a scripter / programmer, but trying to learn a new skill.
Attempting to write a playbook to automate updating of our fleet of Cisco switch stacks but I think I am both lost in syntax and if this is the 'right' way to go about what I am doing.
---
- name: Update Cisco switch stack
hosts: Cisco2960
vars:
upgrade_ios_version: "15.2(7)E5"
tasks:
name: Check current IOS version / Determine if update is needed...
ios_facts:
debug:
msg:
- "Current image is {{ ansible_net_version }}"
- "Current compliant image is {{ upgrade_ios_version }}"
name: Fail if versions match.
ansible.builtin.fail: msg="IOS versions match. Stopping update."
when: "{{ ansible_net_version }} = {{ upgrade_ios_version }}"
At first I thought each variable needed its own quotation, but that appears to be incorrect syntax as well, as below.
when: "{{ ansible_net_version }}" = "{{ upgrade_ios_version }}"
Couple questions:
Is there an easier way with a plain-English way of describing the type of error handling I am looking for? Ansible documentation is great on options, but light on practical applications / examples.
Why am I receiving this specific syntax error in this case?
You can use the playbook below.
Ansible Playbook to upgrade Cisco IOS
- name: Upgrade CISCO IOS
hosts: SWITCHES
vars:
upgrade_ios_version: 15.2(7)E5
tasks:
- name: CHECK CURRENT VERSION
ios_facts:
- debug:
msg:
- "Current version is {{ ansible_net_version }}"
- "Current compliant image is {{ upgrade_ios_version }}"
- debug:
msg:
- "Image is not compliant and will be upgraded"
when: ansible_net_version != upgrade_ios_version
Create backup folder for today
- hosts: localhost
tasks:
- name: Get ansible date/time facts
setup:
filter: "ansible_date_time"
gather_subset: "!all"
- name: Store DTG as fact
set_fact:
DTG: "{{ ansible_date_time.date }}"
- name: Create Directory {{hostvars.localhost.DTG}}
file:
path: ~/network-programmability/backups/{{hostvars.localhost.DTG}}
state: directory
run_once: true
Backup Running Config
- hosts: SWITCHES
tasks:
- name: Backup Running Config
ios_command:
commands: show run
register: config
- name: Save output to ~/network-programmability/backups/
copy:
content: "{{config.stdout[0]}}"
dest: "~/network-programmability/backups/{{hostvars.localhost.DTG}}/{{ inventory_hostname }}-{{hostvars.localhost.DTG}}-config.txt"
SAVE the Running Config
- name: Save running config
ios_config:
save_when: always
Copy software to target device
- name: Copy Image // This could take up to 4 minutes
net_put:
src: "~/network-programmability/images/c2960l-universalk9-mz.152-7.E5.bin"
dest: "flash:/c2960l-universalk9-mz.152-7.E5.bin"
vars:
ansible_command_timeout: 600
Change the Boot Variable to the new image
- name: Change Boot Variable to new image
ios_config:
commands:
- "boot system flash:c2960l-universalk9-mz.152-7.E5.bin"
save_when: always
Reload the device
- name: Reload the Device
cli_command:
command: reload
prompt:
- confirm
answer:
- 'y'
Wait for Reachability to the device
- name: Wait for device to come back online
wait_for:
host: "{{ inventory_hostname }}"
port: 22
delay: 90
delegate_to: localhost
Check current image
- name: Check Image Version
ios_facts:
- debug:
msg:
- "Current version is {{ ansible_net_version }}"
- name: ASSERT THAT THE IOS VERSION IS CORRECT
vars:
upgrade_ios_version: 15.2(7)E5
assert:
that:
- upgrade_ios_version == ansible_net_version
- debug:
msg:
- "Software Upgrade has been completed"

Access hosts in play filtered by task

I have a task that checks the redis service status on the host list below
- hosts: 192.168.0.1, 192.168.0.2, 192.168.0.3
tasks:
- command:
cmd: service redis-server status
register: result
- debug:
var: result
After checking I need to access hosts where service does not exist.
And they should be accessible as variable to proceed with them in the next tasks.
Can someone please help?
Similar to Ansible facts it is also possible to gather service_facts. In example
- name: Set facts SERVICE
set_fact:
SERVICE: "redis-server.service"
- name: Gathering Service Facts
service_facts:
- name: Show ansible_facts.services
debug:
msg:
- "{{ ansible_facts.services[SERVICE].status }}"
If you like to perform tasks after on a service of which you don't the status, with Conditionals you can check the state.
If the service is not installed at that time, the specific variable (key) would not be defined. You would perform Conditionals based on variables then
when: ansible_facts.services[SERVICE] is defined
when: ansible_facts.services['redis-server.service'] is defined
Also it is recommend to use the Ansible service module to perform tasks on the service
- name: Start redis-server, if not started
service:
name: redis-server
state: started
instead of using the command module.
Further services related Q&A
How to check service exists and is not installed in the server using service_facts module in an Ansible playbook?
Ansible: How to start stopped services?
Ansible: How to get disabled but running services?
How to list only the running services with ansible_facts?
Finally found the solution that perfectly matches.
- name: Check that redis service exists
systemd:
name: "redis"
register: redis_status
changed_when: redis_status.status.ActiveState == "inactive"
- set_fact:
_dict: "{{ dict(ansible_play_hosts|zip(
ansible_play_hosts|map('extract', hostvars, 'redis_status'))) }}"
run_once: true
- set_fact:
_changed: "{{ (_dict|dict2items|json_query('[?value.changed].key'))| join(',') }}"
run_once: true

Ansible how restart service one after one on different hosts

Hy every one !
pls help .
I have 3 server. Each of them have one systemd service. I need reboot this service by one.
So after i reboot service on host 1 , and this service is up ( i can check tcp port), i need reboot service on host 2 and so on.
How i can do it with ansible
Now i have such playbook:
---
- name: Install business service
hosts: business
vars:
app_name: "e-service"
app: "{{ app_name }}-{{ tag }}.war"
app_service: "{{ app_name }}.service"
app_bootstrap: "{{ app_name }}_bootstrap.yml"
app_folder: "{{ eps_client_dir }}/{{ app_name }}"
archive_folder: "{{ app_folder }}/archives/arch_{{ansible_date_time.date}}_{{ansible_date_time.hour}}_{{ansible_date_time.minute}}"
app_distrib_dir: "{{ eps_distrib_dir }}/{{ app_name }}"
app_dependencies: "{{ app_distrib_dir }}/dependencies.tgz"
tasks:
- name: Copy app {{ app }} to {{ app_folder }}
copy:
src: "{{ app_distrib_dir }}/{{ app }}"
dest: "{{ app_folder }}/{{ app }}"
group: ps_group
owner: ps
mode: 0644
notify:
- restart app
- name: Copy service setting to /etc/systemd/system/{{app_service}}
template:
src: "{{ app_distrib_dir }}/{{ app_service }}"
dest: /etc/systemd/system/{{ app_service }}
mode: 0644
notify:
- restart app
- name: Start service {{ app }}
systemd:
daemon-reload: yes
name: "{{ app_service }}"
state: started
enabled: true
handlers:
- name: restart app
systemd:
daemon-reload: yes
name: "{{ app_service }}"
state: restarted
enabled: true
and all service restart at one time.
try serial and max_fail_percentage, max_fail_percentage value is percent of the whole number of your hosts, if server 1 failed, then the rest server will not run,
---
- name: Install eps-business service
hosts: business
serial: 1
max_fail_percentage: 10
Try with
---
- name: Install eps-business service
hosts: business
serial: 1
By default, Ansible will try to manage all of the machines referenced in a play in parallel. For a rolling update use case, you can define how many hosts Ansible should manage at a single time by using the serial keyword
https://docs.ansible.com/ansible/latest/user_guide/playbooks_delegation.html
create a script that reboots your service and then add a loop that will check whether the service is up or not and once it is executed successfully based on the status it has returned you can go for the next service.

Ansible - How to loop and get attributes of new EC2 instances

I'm creating a set of new EC2 instances using this play
- hosts: localhost
connection: local
gather_facts: False
tasks:
- name: Provision a set of instances
ec2:
assign_public_ip: yes
aws_access_key: XXXXXXXXXX
aws_secret_key: XXXXXXXXXX
group_id: XXXXXXXXXX
instance_type: t2.micro
image: ami-32a85152
vpc_subnet_id: XXXXXXXXXX
region: XXXXXXXXXX
user_data: "{{ lookup('file', '/SOME_PATH/cloud-config.yaml') }}"
wait: true
exact_count: 1
count_tag:
Name: Demo
instance_tags:
Name: Demo
register: ec2
- name: Add new CoreOS machines to coreos-launched group
add_host: hostname="{{ item.public_ip }}" groups=coreos-launched
with_items: "{{ ec2.instances }}"
- name: Wait for SSH to come up
wait_for: host="{{ item.public_dns_name }}" port=22 delay=60 timeout=320
with_items: "{{ ec2.instances }}"
Now, I need to create an SSL/TLS certificate for every of those new machines. To do so, I require their private IP. Yet, I don't know how to access the "{{ ec2.instances }}" I registered in the previous play.
I tried something, in the same playbook, to do something like this
- hosts: coreos-launched
gather_facts: False
tasks:
- name: Find the current machine IP addresse
command: echo "{{ item.private_ip }}" > /tmp/private_ip
with_items: "{{ ec2.instances }}"
sudo: yes
But without any success. Is there a way to use the "{{ ec2.instances }}" items inside a same playbook but in a different play?
-- EDIT --
Following Theo advices, I manage to get the instances attributes using
- name: Gather current facts
action: ec2_facts
register: ec2_facts
- name: Use the current facts
command: echo "{{ ec2_facts.ansible_facts.ansible_ec2_local_ipv4 }}"
with_items: "{{ ec2_facts }}"
The best way to learn about a return structure (short of the documentation) is to wrap it in a debug task.
- name: Debug ec2 variable
debug: var=ec2.instances
From there, follow the structure to get the variable you seek.
Also (following the idempotence model), you can use the ec2_remote_facts module to get the facts from the instances and call them in future plays/tasks as well.
See Variables -- Ansible Documentation for more info about calling registered variables.

Resources