Replace a line in a config file using variables with ansible - deployment

Question is similar to this one: Replace a line in a config file with ansible . Difference is that my playbook is first copying a file to a destination and then editing that same file after it's been copied. Also I'm using variables to replace the string, however it isn't changing the lines that contain the particular string site_name in the conf file.
Playbook;
---
- hosts: server-test2
become: true
vars:
site_name: bokucasinon.com
tasks:
- name: Configuring nginx for the new site
template:
src: ../provision-server/nginx.j2
dest: /etc/nginx/conf.d/{{site_name}}.conf
mode: 064
- name: Configuring nginx for the new site
become: true
lineinfile:
dest: /etc/nginx/conf.d/{{site_name}}.conf
regexp: '^(.*)site_name(.*)$'
line: "{{site_name}}"
backrefs: yes
Output:
TASK [Configuring nginx for the new site] **************************************************************
task path: /home/melvmagr/repos/ansible/provision-server/wp-db-nginx-conf.yml:10
ok: [server-test2] => {"changed": false, "checksum": "904d19dde94ad38672d751246fd2680ce297244d", "dest": "/etc/nginx/conf.d/bokucasinon.com.conf", "gid": 0, "group": "root", "mode": "0064", "owner": "root", "path": "/etc/nginx/conf.d/bokucasinon.com.conf", "size": 4232, "state": "file", "uid": 0}
TASK [Configuringg nginx for the new site] *************************************************************
task path: /home/melvmagr/repos/ansible/provision-server/wp-db-nginx-conf.yml:15
ok: [server-test2] => {"backup": "", "changed": false, "msg": ""}
META: ran handlers
META: ran handlers
PLAY RECAP *********************************************************************************************
server-test2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As one can see, changed=0 and upon checking the conf file it remains site_name instead of bokucasinon.com
Another thing I tried was to use the replace module but got same output.
replace:
path: /etc/nginx/conf.d/{{site_name}}.conf
regexp: '(^site_name)(.*)$'
replace: '{{site_name}}'
Any ideas why this is happening or what I'm doing wrong?
Thanks in advance

Appreciate all of you for the help but I've managed to find what I was looking for, after lots of trials and errors. I did indeed need to use the ansible.builtin.replace module. Apparently what I was using (the lineinfile module) was not made for changing ALL the lines that contain a particular string (reference: https://www.middlewareinventory.com/blog/ansible-lineinfile-examples/) so basically just to put things into perspective, I needed to change my playbook with the following;
- name: Configuring nginx for the new site
become: true
template:
src: ../provision-server/nginx.j2
dest: /etc/nginx/conf.d/{{site_name}}.conf
mode: 064
- name: Configuring nginx for the new site
become: yes
become_user: root
ansible.builtin.replace:
path: /etc/nginx/conf.d/{{site_name}}.conf
regexp: 'sitename.com'
replace: "{{site_name}}"

Related

Ansible: How to read file and push results to templates

I have task to read data from csv file and push result to templates and copy those templates to different servers. however, i am getting error while writing to template. below are details -
main.yml
- name: Print return information from the previous task
vars:
test_csv: "{{ lookup('file', '/u00/ansible/Playbooks/files/newrelic_test.csv', wantlist=True) }}"
ansible.builtin.debug:
var: test_csv
- name: copy template
template:
src: /u00/ansible/Playbooks/files/infra-config.yml_template
dest: /u00/app/monitor/infra-config.yml
with_items: test_csv
notify: confirm copy done
- name: Start the New Relic Service
ansible.builtin.systemd:
name: infra.service
state: started
become: yes
become_user: root
infra-config.yml_template -
custom_attributes:
application : {{ item.Application }}
env : {{ item.env }}
datacenter : {{ item.Datacenter }}
log:
file: /u00/app/monitor/infra.log
csv file content
Application,Host,env,Datacenter
Microsoft,testserver1,TEST,DC1
Apple,testserver2,TEST,DC2
error -
> TASK [config-update : copy template]
> ******************************************* [0;31mAn exception occurred during task execution. To see the full traceback, use -vvv.
> The error was: ansible.errors.AnsibleUndefinedVariable:
> 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute
> 'db_name'[0m [0;31mfailed: [testserver1]
> (item=test_csv) => {"ansible_loop_var": "item", "changed": false,
> "item": "test_csv", "msg": "AnsibleUndefinedVariable:
> 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute
> 'db_name'"}
Expectation is to read csv file and use variables in template in different servers.
testserver1 -
> custom_attributes: application : Microsoft env : Test datacenter : DC1
> log: file: /u00/app/monitor/infra.log
testserver2 -
> custom_attributes: application : Apple env : Test datacenter : DC1
> log: file: /u00/app/monitor/infra.log
There are few things to fix in your playbook. First, you are defining your test_csv variable inside a task and it cannot be accessible by other tasks. You can use register instead. However, the first task returns a list but with one string like this "test_csv": ["Application,Host,env,Datacenter\nMicrosoft,testserver1,TEST,DC1 \nApple,testserver2,TEST,DC2"] which only results one item in the test_csv list.
You can achieve this by using read_csv module as well. Below I demonstrate how:
Note that I have added a condition using inventory_hostname on the copy template task since you might want to target each csv line according to its hostname. You can modify this according to your needs.
Csv file content:
Application,Host,env,Datacenter
Microsoft,localhost,TEST,DC1
Apple,testserver2,TEST,DC2
Example of playbook for testing:
- name: Check status
hosts: localhost
gather_facts: no
tasks:
- name: read csv file and return a list
read_csv:
path: test.csv
register: applications
- name: Ouput applications from previous task
debug:
msg: "{{ item.Application }}"
loop: "{{ applications.list }}"
- name: copy template
template:
src: src.yml_template ##I would recommendr to use .j2 jinja template instead.
dest: dest.yml
loop: "{{ applications.list }}"
when: inventory_hostname == item.Host
src.yml_template content:
custom_attributes:
application : {{ item.Application }}
env : {{ item.env }}
datacenter : {{ item.Datacenter }}
log:
file: /u00/app/monitor/infra.log
Gives in dest.yml:
custom_attributes:
application : Microsoft
env : TEST
datacenter : DC1
log:
file: /u00/app/monitor/infra.log
Cli output:
PLAY [Check status] **********************************************************************************************************************************************************
TASK [read csv file and return a list] ***************************************************************************************************************************************
ok: [localhost]
TASK [Ouput applications from previous task] *********************************************************************************************************************************
ok: [localhost] => (item={'Application': 'Microsoft', 'Host': 'localhost', 'env': 'TEST', 'Datacenter': 'DC1 '}) => {
"msg": "Microsoft"
}
ok: [localhost] => (item={'Application': 'Apple', 'Host': 'testserver2', 'env': 'TEST', 'Datacenter': 'DC2'}) => {
"msg": "Apple"
}
TASK [copy template] *********************************************************************************************************************************************************
changed: [localhost] => (item={'Application': 'Microsoft', 'Host': 'localhost', 'env': 'TEST', 'Datacenter': 'DC1 '})
skipping: [localhost] => (item={'Application': 'Apple', 'Host': 'testserver2', 'env': 'TEST', 'Datacenter': 'DC2'})

Can You Set a Nested Environment Variable in Kubernetes?

I have a pod running dotnet that leverages an appsettings.json file. I have the following entry for RabbitMq:
appsettings.json
{
...
"RabbitMQ": {
"HostName": "localhost",
"UserName": "someuser",
"Password": "somepassword"
}
}
I am trying to update the RabbitMQ.HostName property within my deployment yaml like so:
env:
- name: "RabbitMQ:HostName"
value: "rabbitmq-cluster-deployment.rabbitmq.svc.cluster.local"
It doesn't work. I have tried different variations but nothing looks like it sets it.
Does Kubernetes have a way of setting the "nested property" or no? I am aware that the : character is not allowed. I have tried using . which didn't throw an error, but also didn't work. The reason I was thinking it was a : is because that is how you would do it with dotnet.
Example: _configuration["RabbitMQ:HostName"]
Other "non-nested" environment variables are set just fine.
Remove the quotes from the name field and replace : with double underscores __
Instead of
env:
- name: "RabbitMQ:HostName"
value: "rabbitmq-cluster-deployment.rabbitmq.svc.cluster.local"
use
env:
- name: RabbitMQ__HostName
value: "rabbitmq-cluster-deployment.rabbitmq.svc.cluster.local"

Mystery "guest" user for rabbitMQ

I know the "guest" user is the default for RabbitMQ, but I thought I'd configured everything to use different names.
My stack is Django / Celery / RabbitMQ, running in Docker.
First up, the error - I jst get loads of these - every few seconds:
rabbitmq_1 | 2020-07-29 08:28:00.775 [warning] <0.1234.0> HTTP access denied: user 'guest' - invalid credentials
rabbitmq_1 | 2020-07-29 08:28:05.775 [warning] <0.1240.0> HTTP access denied: user 'guest' - invalid credentials
rabbitmq_1 | 2020-07-29 08:28:10.776 [warning] <0.1246.0> HTTP access denied: user 'guest' - invalid credentials
rabbitmq_1 | 2020-07-29 08:28:15.776 [warning] <0.1252.0> HTTP access denied: user 'guest' - invalid credentials
rabbitMQ Dockerfile
FROM rabbitmq:management-alpine
ENV RABBITMQ_USER rabbit_user
ENV RABBITMQ_PASSWORD rabbit_user
ADD rabbitmq.conf /etc/rabbitmq/
ADD definitions.json /etc/rabbitmq/
RUN chown rabbitmq:rabbitmq /etc/rabbitmq/rabbitmq.conf /etc/rabbitmq/definitions.json
CMD ["rabbitmq-server"]
rabbitmq.conf
management.load_definitions = /etc/rabbitmq/definitions.json
definitions.json
{
"users": [
{
"name": "rabbit_user",
"password": "rabbit_user",
"tags": ""
},
{
"name": "admin",
"password": "admin",
"tags": "administrator"
}
],
"vhosts": [
{
"name": "\/phoenix"
}
],
"permissions": [
{
"user": "rabbit_user",
"vhost": "\/phoenix",
"configure": ".*",
"write": ".*",
"read": ".*"
}
],
"parameters": [],
"policies": [],
"exchanges": [],
"bindings": [],
"queues": [
{
"name": "high_prio",
"vhost": "\/phoenix",
"durable": true,
"auto_delete": false,
"arguments": {}
},
{
"name": "low_prio",
"vhost": "\/phoenix",
"durable": true,
"auto_delete": false,
"arguments": {}
}
]
}
docker-compose.yml
rabbitmq:
build:
context: ./rabbitmq
dockerfile: Dockerfile
# image: rabbitmq:3-management-alpine
ports:
- "15672:15672" # RabbitMQ management plugin
environment:
- RABBITMQ_DEFAULT_USER=rabbit_user
- RABBITMQ_DEFAULT_PASS=rabbit_user
- RABBITMQ_DEFAULT_VHOST=phoenix
expose:
- "5672" # Port exposed between docker containers
depends_on:
- db
- cache
celery_worker:
<<: *django
command: bash -c "celery -A phoenix.celery worker --loglevel=INFO -n worker1#%h"
environment:
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD}
- DJANGO_SETTINGS=${DJANGO_SETTINGS}
# HC the rabbit user. Not secure obvs, but OK for PoC.
- RABBITMQ_DEFAULT_USER=rabbit_user
- RABBITMQ_DEFAULT_PASS=rabbit_user
ports: []
links:
- rabbitmq
- cache
depends_on:
- db
- cache
- rabbitmq
settings.py
CELERY_BROKER_URL = "amqp://rabbit_user:rabbit_user#rabbitmq:5672/phoenix"
CELERY_BROKER_VHOST = "phoenix"
CELERY_RESULT_BACKEND = "django-db"
CELERY_CACHE_BACKEND = "default"
CELERY_TIME_ZONE = TIME_ZONE
I had it all working before when I just pulled the default rabbitMQ container in the docker-compose yaml file. Now I've created a specific Dockerfile for rabbitMQ, and setup rabbit_user and the vhost "phoenix". It all seems to be working - tasks are run, I see the message stats in the rabbit console, but I'm suffering these random "guest" login attempts. The word "guest" appears nowhere in my codebase, so somewhere RabbitMQ is using the default not "rabbit_user", but I can't see where.
Rather typical that I solve this by "fixing" something else ..
I noticed in my RMQ panel that the low_prio and high_prio queues had vhost "/phoenix", while the celery workers had vhost "phoenix" (I'd thought the RMQ config required the leading slash from my reading). I amended this so that all queues were allocated to "phoenix", and the mystery guest login disappeared.
I can only assume that since Celery was configured for the vhost "phoenix", that "/phoenix" was treated as s different vhost, with no users assigned to it, so RabbitMQ tried to use the "guest" default.
Not entirely sure why things were connecting to it - I'd sent nothing to those queues yet - but in case somebody else has this issue, this is what solved it for me.

Having issues with rs.add() in ansible playbook for mongo

I am using below tasks in my playbook to initialize cluster and add secondary to primary:
- name: Initialize replica set
run_once: true
delegate_to: host1
shell: >
mongo --eval 'printjson(rs.initiate())'
- name: Format secondaries
run_once: true
local_action:
module: debug
msg: '"{{ item }}:27017"'
with_items: ['host2', 'host3']
register: secondaries
- name: Add secondaries
run_once: true
delegate_to: host1
shell: >
/usr/bin/mongo --eval 'printjson(rs.add({{ item.msg }}))'
with_items: secondaries.results
I am getting below error:
TASK [mongodb-setup : Add secondaries] *******************************
fatal: [host1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'msg'\n\nThe error appears to have been in '/var/lib/awx/projects/_dev/roles/mongodb-setup/tasks/users.yml': line 15, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Add secondaries\n ^ here\n"}
Thanks for the response, I have amended my code as below
- name: Add secondaries
run_once: true
delegate_to: host-1
shell: >
/usr/bin/mongo --eval 'printjson(rs.add({{ item }}:27017))'
with_items:
- host2
- host3
but getting below error
failed: [host-2 -> host-1] (item=host-2) => {"changed": true, "cmd": "/usr/bin/mongo --eval 'printjson(rs.add(host-2:27017))'", "delta": "0:00:00.173077", "end": "2019-08-06 13:29:09.422560", "item": "host-2", "msg": "non-zero return code", "rc": 252, "start": "2019-08-06 13:29:09.249483", "stderr": "", "stderr_lines": [], "stdout": "MongoDB shell version: 3.2.22\nconnecting to: test\n2019-08-06T13:29:09.419-0500 E QUERY [thread1] SyntaxError: missing ) after argument list #(shell eval):1:37", "stdout_lines": ["MongoDB shell version: 3.2.22", "connecting to: test", "2019-08-06T13:29:09.419-0500 E QUERY [thread1] SyntaxError: missing ) after argument list #(shell eval):1:37"]}
You issue is not with rs.add() but with the data you loop over. In your last task, your item list is a single string.
# Wrong #
with_items: secondaries.results
You want to pass an actual list form your previously registered result:
with_items: "{{ secondaries.results }}"
That being said, registering the result of a debug task is rather odd. You should use set_fact to register what you need in a var, or better directly loop other your list of hosts in your task. It also looks like the rs.add funcion is exepecting a string so you should quote the argument in your eval. Something like:
- name: Add secondaries
shell: >
/usr/bin/mongo --eval 'printjson(rs.add("{{ item }}:27017"))'
with_items:
- host2
- host3
And the way you use delegation seems rather strange to me in this context but it's hard to give any valid clues without a complete playbook example of what you are trying to do (that you might give in a new question if necessary).

Is it possible to change a variable's value in ansible?

I wrote a playbook that read a content of two files. The first one is responsible for holding switches interfaces dynamically that have the protocol CDP.
example.cdp:
0/0
14/0
The second one (.cfg), is a file the contains also dynamically a bunch of interfaces that I need to push to a device using the cisco command "shutdown" to test my master/backup environment. If the interfaces of the example.cdp are here, I need to remove them because I cannot lose the communication with this device since the management is in-band.
example.cfg:
interface FastEthernet0/0
shutdown
interface FastEthernet1/0
shutdown
interface FastEthernet2/0
shutdown
interface FastEthernet2/1
shutdown
...
interface FastEthernet14/0
shutdown
playbook:
- name: Looping file
debug:
msg: "{{ item }}"
register: items
with_file:
- ~/ANSIBLE/{{ inventory_hostname }}.cfg
- debug: var=items.results[0].item
- name: capturing interfaces with cdp
raw: egrep '[0-9]+\/[0-9]+ ' -o ~/ANSIBLE/{{ inventory_hostname }}.cdp
register: cdp
- debug: var=cdp.stdout_lines
- set_fact:
cdp: "{{cdp.stdout_lines}}"
- debug: var=cdp
- name: Removing interfaces with cdp
raw: sed 's/interface FastEthernet{{item}}//' ~/ANSIBLE/{{ inventory_hostname }}.cfg
with_items:
- "{{cdp}}"
register: items
- debug: var=items
- name: Applying The Shutdown Template
ios_config:
lines:
- "{{ items.results[0].item }}"
provider: "{{cli}}"
register: shut1
- debug: var=shut1
tags: shut1
running the playbook:
<169.255.0.1> EXEC sed 's/interface FastEthernet0/0 //' ~/ANSIBLE /169.255.0.1.cfg
failed: [169.255.0.1] (item=0/0 ) => {
"changed": true,
"failed": true,
"item": "0/0 ",
"rc": 1,
"stderr": "sed: -e expression #1, char 30: unknown option to `s'\n",
"stdout": "",
"stdout_lines": []
}
<169.255.0.1> EXEC sed 's/interface FastEthernet14/0 //' ~/ANSIBLE/169.255.0.1.cfg
failed: [169.255.0.1] (item=14/0 ) => {
"changed": true,
"failed": true,
"item": "14/0 ",
"rc": 1,
"stderr": "sed: -e expression #1, char 31: unknown option to `s'\n",
"stdout": "",
"stdout_lines": []
}
As you can see, the problem is the content of the var "cdp". The interfaces have the symbol "/", wich is use in "sed" command and I should backslashed this one to solve my problem using ansible. Is there a way to open a variable and make some regsub on it?
sed can use any character as the regex tokenizer, so solve your issue quickly, turn it into (for instance using # character):
sed 's#interface FastEthernet{{item}}##' ~/ANSIBLE/{{ inventory_hostname }}.cfg
I have the impression templating would be a better way to write your tasks though.