Ansible: Jinja2 doesn't read dictionary object correctly - python-3.x

EDIT: I HAD THE WRONG FILE OPEN IN ATOM, CONTINUE TO MY ANSWER TO KNOW HOW.
I have a playbook that is supposed to spin up websites for three fictional company names, however in one of my Jinja2 templates, it doesn't find a variable that is clearly located in a dictionary. Any thoughts are greatly appreciated!
Ansible Version:
ansible 2.10.13
config file = None
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.6.8 (default, Jan 27 2021, 01:17:18) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]
playbook.yml
---
- hosts: company_servers
vars:
company_site:
totsuki:
companyname: UA High School
apache_dir: totsuki_academy
filename: totsuki_academy
companyid: 1
uahs:
companyname: UA High School
apache_dir: ua_high_school
filename: ua_highschool
companyid: 2
we:
companyname: Wayne Enterprises
apache_dir: wayne_enterprises
filename: ua_highschool
companyid: 3
tasks:
- name: Install Apache
dnf:
name: httpd
state: latest
- name: Create Website Folders
file:
path: /var/www/html/{{ item.value.apache_dir }}
state: directory
mode: 0775
with_dict: "{{company_site}}"
- name: Create HTML files
template:
src: htmltemplate2.html.j2
dest: /var/www/html/{{ item.value.apache_dir }}/index.html
owner: apache
group: apache
mode: 0775
with_dict: "{{company_site}}"
- name: Apache Preference Import
template:
src: httpd.conf
dest: /etc/httpd/conf/httpd.conf
mode: 0775
- name: Create Configuration Folders
file:
path: /etc/httpd/{{ item }}
state: directory
mode: 0775
loop:
- sites-enabled
- sites-available
- name: Apache Preference Import
template:
src: httpd.conf
dest: /etc/httpd/sites-available
mode: 0775
- name: Create a symbolic link
file:
src: /etc/httpd/sites-available
dest: /etc/httpd/sites-enabled
state: link
- name: Restart Apache
service:
name: httpd
state: restarted
- name: Stop Firewall
service:
name: firewalld
state: stopped
Infuriating Error:
The full traceback is:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/ansible/template/__init__.py", line 1066, in do_template
res = j2_concat(rf)
File "<template>", line 14, in root
File "/usr/local/lib/python3.6/site-packages/jinja2/runtime.py", line 903, in _fail_with_undefined_error
raise self._undefined_exception(self._undefined_message)
jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'companyname'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/ansible/plugins/action/template.py", line 139, in run
resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)
File "/usr/local/lib/python3.6/site-packages/ansible/template/__init__.py", line 1103, in do_template
raise AnsibleUndefinedVariable(e)
ansible.errors.AnsibleUndefinedVariable: 'dict object' has no attribute 'companyname'
failed: [192.168.100.6] (item={'key': 'we', 'value': {'companyname': 'Wayne Enterprises', 'apache_dir': 'wayne_enterprises', 'filename': 'ua_highschool', 'companyid': 3}}) => {
"ansible_loop_var": "item",
"changed": false,
"item": {
"key": "we",
"value": {
"apache_dir": "wayne_enterprises",
"companyid": 3,
"companyname": "Wayne Enterprises",
"filename": "ua_highschool"
}
},
"msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'companyname'"
}
The full traceback is:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/ansible/template/__init__.py", line 1066, in do_template
res = j2_concat(rf)
File "<template>", line 14, in root
File "/usr/local/lib/python3.6/site-packages/jinja2/runtime.py", line 903, in _fail_with_undefined_error
raise self._undefined_exception(self._undefined_message)
jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'companyname'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/ansible/plugins/action/template.py", line 139, in run
resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)
File "/usr/local/lib/python3.6/site-packages/ansible/template/__init__.py", line 1103, in do_template
raise AnsibleUndefinedVariable(e)
ansible.errors.AnsibleUndefinedVariable: 'dict object' has no attribute 'companyname'
failed: [192.168.100.7] (item={'key': 'we', 'value': {'companyname': 'Wayne Enterprises', 'apache_dir': 'wayne_enterprises', 'filename': 'ua_highschool', 'companyid': 3}}) => {
"ansible_loop_var": "item",
"changed": false,
"item": {
"key": "we",
"value": {
"apache_dir": "wayne_enterprises",
"companyid": 3,
"companyname": "Wayne Enterprises",
"filename": "ua_highschool"
}
},
"msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'companyname'"
}
Also, I have noticed that Wayne enterprises 'filename' wasn't correct and I did adjust that.
It says object has no attribute 'companyname'
and here is my html.j2
<html>
<head>
<title>{{ item.value.companyname }}</title>
</head>
<body>
<h1>Welcome to the {{ item.value.companyname }} website</h1>
IP Address: {{ ansible_default_ipv4.address }}
</body>
</html>

This was solved by taking a look at the path of the file I had open in Atom, and comparing it to the path of the file that Ansible was running. It's important to know, that when you're using the template module in Ansible, that files you're referencing without an exact path should be stored in ~/templates and Ansible will make that folder for you upon running a playbook with the template module.
Common sense in my mind thought it might reference the directory of the playbook, but is not how Ansible works.

Related

define variable list as a file in ansible playbook

Can someone help me in finding the best way to declare all variables inside a file and define the file path in ansible playbook? Here's my ansible-playbook
---
- hosts: myhost
vars:
- var: /root/dir.txt
tasks:
- name: print variables
debug:
msg: "username: {{ username }}, password: {{ password }}"
These are the contents inside dir.txt
username=test1
password=mypassword
When I run this, I am facing an error
TASK [print variables] *********************************************************************************************************
fatal: [121.0.0.7]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'username' is undefined\n\nThe error appears to be in '/root/test.yml': line 6, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: print variables\n ^ here\n"}
Expected output is to print the variables by this method
Any help would be appreciated
There are two reasons why your code won't work.
A variables file should be in YAML or JSON format for Ansible to load it
Such variables files are loaded with vars_files directive or include_vars task, rather than vars
So a YAML file named /root/dir.yml:
username: test1
password: mypassword
Can be used in a playbook like:
---
- hosts: myhost
vars_files:
- /root/dir.yml
tasks:
- name: print variables
debug:
msg: "username: {{ username }}, password: {{ password }}"

Unable to run Python Script from within an Ansible Playbook

I am trying to write an ansible playbook to crawl a website and then store its contents into a static file under aws s3 bucket. Here is the crawler code :
"""
Handling pages with the Next button
"""
import sys
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://xyz.co.uk/"
file_name = "web_content.txt"
while True:
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
raw_html = soup.prettify()
file = open(file_name, 'wb')
print('Collecting the website contents')
file.write(raw_html.encode())
file.close()
print('Saved to %s' % file_name)
#print(type(raw_html))
# Finding next page
next_page_element = soup.select_one('li.next > a')
if next_page_element:
next_page_url = next_page_element.get('href')
url = urljoin(url, next_page_url)
else:
break
This is my ansible-playbook:
---
- name: create s3 bucket and upload static website content into it
hosts: localhost
connection: local
tasks:
- name: create a s3 bucket
amazon.aws.aws_s3:
bucket: testbucket393647914679149
region: ap-south-1
mode: create
- name: create a folder in the bucket
amazon.aws.aws_s3:
bucket: testbucket393647914679149
object: /my/directory/path
mode: create
- name: Upgrade pip
pip:
name: pip
version: 21.1.3
- name: install virtualenv via pip
pip:
requirements: /root/ansible/requirements.txt
virtualenv: /root/ansible/myvenv
virtualenv_python: python3.6
environment:
PATH: "{{ ansible_env.PATH }}:{{ ansible_user_dir }}/.local/bin"
- name: Run script to crawl the website
script: /root/ansible/beautiful_crawl.py
- name: copy file into bucket folder
amazon.aws.aws_s3:
bucket: testbucket393647914679149
object: /my/directory/path/web_content.text
src: web_content.text
mode: put
Problem is when I run this, it runs fine upto the task name: install virtualenv via pip and then throws following error while executing the task name: Run script to crawl the website:
fatal: [localhost]: FAILED! => {"changed": true, "msg": "non-zero return code", "rc": 2, "stderr": "/root/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-9798 3643645466/beautiful_crawl.py: line 1: import: command not found\n/root/.ansible /tmp/ansible-tmp-1625137700.8854306-13026-97983643645466/beautiful_crawl.py: lin e 2: from: command not found\n/root/.ansible/tmp/ansible-tmp-1625137700.8854306- 13026-97983643645466/beautiful_crawl.py: line 3: import: command not found\n/roo t/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-97983643645466/beautiful_cra wl.py: line 4: from: command not found\n/root/.ansible/tmp/ansible-tmp-162513770 0.8854306-13026-97983643645466/beautiful_crawl.py: line 6: url: command not foun d\n/root/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-97983643645466/beauti ful_crawl.py: line 7: file_name: command not found\n/root/.ansible/tmp/ansible-t mp-1625137700.8854306-13026-97983643645466/beautiful_crawl.py: line 10: syntax e rror near unexpected token ('\n/root/.ansible/tmp/ansible-tmp-1625137700.885430 6-13026-97983643645466/beautiful_crawl.py: line 10: response = requests.get (url)'\n", "stderr_lines": ["/root/.ansible/tmp/ansible-tmp-1625137700.8854306-1 3026-97983643645466/beautiful_crawl.py: line 1: import: command not found", "/ro ot/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-97983643645466/beautiful_cr awl.py: line 2: from: command not found", "/root/.ansible/tmp/ansible-tmp-162513 7700.8854306-13026-97983643645466/beautiful_crawl.py: line 3: import: command no t found", "/root/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-9798364364546 6/beautiful_crawl.py: line 4: from: command not found", "/root/.ansible/tmp/ansi ble-tmp-1625137700.8854306-13026-97983643645466/beautiful_crawl.py: line 6: url: command not found", "/root/.ansible/tmp/ansible-tmp-1625137700.8854306-13026-97 983643645466/beautiful_crawl.py: line 7: file_name: command not found", "/root/. ansible/tmp/ansible-tmp-1625137700.8854306-13026-97983643645466/beautiful_crawl. py: line 10: syntax error near unexpected token ('", "/root/.ansible/tmp/ansibl e-tmp-1625137700.8854306-13026-97983643645466/beautiful_crawl.py: line 10: response = requests.get(url)'"], "stdout": "", "stdout_lines": []}
What am I doing wrong here?
You have multiple problems.
Check the documentation.
No. 1: The script modules will run bash scripts by default, not python scripts. If you want to run a python script, you need to add a shebang like #!/usr/bin/env python3 as the first line of the script or use the executable parameter.
No 2: You create a venv, so I assume you want to run the script in that venv. You can't do that out of the box with the script module, so you would need to work around that.
This should work for you (you don't need the shebang, as you tell the script module to run it with python in the venv using the executable parameter):
- name: Run script to crawl the website
script: /root/ansible/beautiful_crawl.py
executable: /root/ansible/myvenv/bin/python

Ansible Help: "msg": "The task includes an option with an undefined variable

I got below error while running my playbook (below)
fatal: [192.168.22.200]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: float object has no element 55\n\nThe error appears to be in '/home/subbu/Downloads/lab/test-02.yml': line 14, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Export configuration\n ^ here\n"}
test-02.yml:
---
- name: Running config
hosts: ALL
connection: local
collections:
- paloaltonetworks.panos
tasks:
- name: Load Variables
include_vars: vars.yml
- name: Export configuration
panos_export:
provider: '{{ provider }}'
category: 'configuration'
filename: 'running-config.xml'
Here is my vars file used in above playbook
vars.yml:
provider:
ip_address: "{{ 192.168.22.200 }}"
username: 'admin'
password: 'seeyousoon'
In your vars.yml, try removing the curly brackets and using single quotes for ip_address.
i.e. ip_address: '192.168.22.200'

Ansible not able to create symlink

I'm trying to create a symlink and I'm not able to solve this error . Please suggest me a solution on how to solve this error
Code: Creating a symlink for /usr/local/bin/terraform-env/bin/* in folder /usr/local/bin
I tried with /usr/local/bin/ (with and without slash)
- name: Move tfenv file:
src: "/usr/local/bin/terraform-env/bin/{{ item.src }}"
dest: "/usr/local/bin/"
state: link
owner: root
group: root
mode: 755
force: yes
with_items:
- src: terraform
- src: tfenv
TASK [terraform : Move tfenv] **************************************************
task path: /opt/ansible/roles/terraform/tasks/main.yml:16
failed: [127.0.0.1] (item={'src': 'terraform'}) => {"changed": false, "gid": 0, "group": "root", "item": {"src": "terraform"}, "mode": "0755", "msg": "the directory /usr/local/bin/ is not empty, refusing to convert it", "owner": "root", "path": "/usr/local/bin/", "size": 4096, "state": "directory", "uid": 0}
failed: [127.0.0.1] (item={'src': 'tfenv'}) => {"changed": false, "gid": 0, "group": "root", "item": {"src": "tfenv"}, "mode": "0755", "msg": "the directory /usr/local/bin/ is not empty, refusing to convert it", "owner": "root", "path": "/usr/local/bin/", "size": 4096, "state": "directory", "uid": 0}
Using ansible 2.8.3
the directory /usr/local/bin/ is not empty, refusing to convert it
You are trying to create the symlink directly on the existing directory rather than creating an entry inside that dir to support the symlink. The following corrected task should get you going:
- name: Move tfenv file:
src: "/usr/local/bin/terraform-env/bin/{{ item.src }}"
dest: "/usr/local/bin/{{ item.src }}"
state: link
owner: root
group: root
mode: 755
force: yes
with_items:
- src: terraform
- src: tfenv

How to create secured files in Puppet5 with Hiera?

I want to create SSL certificate and try to secure this operation.
I am using Puppet 5.5.2 and gem hiera-eyaml.
Created simple manifest
cat /etc/puppetlabs/code/environments/production/manifests/site.pp
package { 'tree':
ensure => installed,
}
package { 'httpd':
ensure => installed,
}
$filecrt = lookup('files')
create_resources( 'file', $filecrt )
Hiera config
---
version: 5
defaults:
# The default value for "datadir" is "data" under the same directory as the hiera.yaml
# file (this file)
# When specifying a datadir, make sure the directory exists.
# See https://puppet.com/docs/puppet/latest/environments_about.html for further details on environments.
datadir: data
data_hash: yaml_data
hierarchy:
- name: "Secret data: per-node, per-datacenter, common"
lookup_key: eyaml_lookup_key # eyaml backend
paths:
- "nodes/%{facts.fqdn}.eyaml"
- "nodes/%{trusted.certname}.eyaml" # Include explicit file extension
- "location/%{facts.whereami}.eyaml"
- "common.eyaml"
options:
pkcs7_private_key: /etc/puppetlabs/puppet/eyaml/keys/private_key.pkcs7.pem
pkcs7_public_key: /etc/puppetlabs/puppet/eyaml/keys/public_key.pkcs7.pem
- name: "YAML hierarchy levels"
paths:
- "common.yaml"
- "nodes/%{facts.fqdn}.yaml"
- "nodes/%{::trusted.certname}.yaml"
And common.yaml
---
files:
'/etc/httpd/conf/server.crt':
ensure: present
mode: '0600'
owner: 'root'
group: 'root'
content: 'ENC[PKCS7,{LOT_OF_STRING_SKIPPED}+uaCmcHgDAzsPD51soM+AIkIlv0ANpUXzBpwM3tqQ3ysFtz81S0xuVbKvslK]'
But have en error while applying manifest
Error: Evaluation Error: Error while evaluating a Function Call, create_resources(): second argument must be a hash (file: /etc/puppetlabs/code/environments/production/manifests/site.pp, line: 12, column: 1) on node test1.com
I really dont know what to do )
The problem appears to be that the indentation in common.yaml isn't right - currently, file will be null rather than a hash, which explains the error message. Also, the file should be called common.eyaml, otherwise the ENC string won't be decrypted. Try
---
files:
'/etc/httpd/conf/server.crt':
ensure: present
mode: '0600'
owner: 'root'
group: 'root'
content: 'ENC[PKCS7{LOTS_OF_STRING_SKIPPED}UXzBpwM3tqQ3ysFtz81S0xuVbKvslK]'
There is an online YAML parser at http://yaml-online-parser.appspot.com/ if you want to see the difference the indentation makes.
Found another solution.
Its was a problem with lookup and hashes. When I have multiply lines in hiera hash, I must specify them https://docs.puppet.com/puppet/4.5/function.html#lookup
So i decided use only 'content' variable to lookup
cat site.pp
$filecrt = lookup('files')
file { 'server.crt':
ensure => present,
path => '/etc/httpd/conf/server.crt',
content => $filecrt,
owner => 'root',
group => 'root',
mode => '0600',
}
and Hiera
---
files:'ENC[PKCS7{LOT_OF_STRING_SKIPPED}+uaCmcHgDAzsPD51soM+AIkIlv0ANpUXzBpwM3tqQ3ysFtz81S0xuVbKvslK]'

Resources