I am writing an ansible playbook that will :
log into a windows box
check if the version of the application i am deploying exists already
downloads the application from git
extracts the app's zip file
moves it to the correct directory
I am having a bunch of trouble on a section of the code where i need to find the extracted directory. I am extracting a GIT zip, and of course, through Github the zip file contains the git hash directory. What I do is i look for this directory through a simple find-like command in windows. The problem is that during debug, i can see the results in stdout_lines ... but in practice, it doesn't seem to work.
Here is the code:
---
# Register our work path to do work in
- name: Registering {{apm_work_path}} as our working path
tags: install
win_stat: path={{apm_work_path}}
register: my_apmworkpath
# First check to see if an agent is already installed in our
# destination. We want to make sure we dont install multiple
# agents
- name: Checking if {{apm_root_path}} path exists on our remote server
tags: install
win_stat: path={{apm_root_path}}
register: my_apmrootpath
# Check if the apm agent is installed in the root path
- name: Checking if {{apm_root_path}}\\{{apm_install_path}} path exists
tags: install
win_stat: path={{apm_root_path}}/{{apm_install_path}}
register: my_apminstallpath
# Check if the apm version is installed in the root path
- name: Checking if {{apm_version}} path exists
tags: install
win_stat: path={{apm_root_path}}\\{{apm_install_path}}\\{{apm_version}}
register: my_apmversionpath
# We also want to make sure that we keep track of the path
# that we want our agent to be placed
- name: An agent already exists on server
tags: install
debug: msg="APM {{apm_version}} is already installed under {{apm_install_path}}"
when: my_apminstallpath.stat.exists == true
# Create our work directory
- name: Creating Work directory
tags: install
win_file: path={{apm_work_path}} state=directory mode=0755
when: my_apmworkpath.stat.exists == false
# Create our application directory
- name: Creating APM Agent directory
tags: install
win_file: path={{apm_root_path}}\\{{apm_install_path}} state=directory mode=0755
when: my_apminstallpath.stat.exists == false
# Create our versioning directory
- name: Create APM version directory
tags: install
win_file: path={{apm_root_path}}\\{{apm_install_path}}\\{{apm_version}} state=directory mode=0775
when: my_apmversionpath.stat.exists == false
# Export our agent from our version control repo (GIT)
- name: Exporting Agent from GIT
tags: install
win_get_url:
url: http://git.sys.a.com/APM/apm-agent-{{apm_version}}-base-win/repository/archive.zip?ref=master
dest: "{{apm_work_path}}\\agent.zip"
when: my_apmversionpath.stat.exists == false
# Make sure we succesfully got our agent downloaded
- name: Checking our agent download
tags: install
win_stat: path={{apm_work_path}}\\agent.zip
register: my_agentarchive
# Create the base-nix directory
- name: Creating base-nix directory
tags: install
win_file: path={{apm_root_path}}\\{{apm_install_path}}\\{{apm_version}}\\base-win state=directory mode=0755
when:
- my_apmversionpath.stat.exists == false
- my_agentarchive.stat.exists == true
# Extract our GIT tarball to our new directory
- name: Extracting Agent
tags: install
win_unzip:
src: "{{apm_work_path}}\\agent.zip"
dest: "{{apm_work_path}}"
rm: True
when:
- my_apmversionpath.stat.exists == false
- my_agentarchive.stat.exists == true
###############################################################
##### THIS SECTION ############################################
# Get the extract directory
- name: Locate the Extracted Directory
tags: install
win_shell: for /d %d in (*apm-agent*) do echo %d
args:
executable: cmd
chdir: "{{apm_work_path}}"
register: extout
###### UNABLE TO SEE RESULTS HERE ############
- debug: msg={{ item }}
with_items: extout.stdout_lines
# Copy data from our work directory to the base-win directory
- name: Migrate Agent Files
tags: install
win_copy:
src: "{{apm_work_path}}\\{{ item }}\\"
dest: "{{apm_root_path}}\\{{apm_install_path}}\\{{apm_version}}\\base-win"
with_items:
extout.stdout_lines
ignore_errors: no
when:
- my_apmversionpath.stat.exists == false
- my_agentarchive.stat.exists == true
# Now create a symbolic link for the agent path
- name: Creating Symlink
tags: install
win_file: src={{apm_root_path}}\\{{apm_install_path}}\\{{apm_version}}\\base-win dest={{apm_root_path}}\\{{apm_install_path}}\\base-win state=link
when:
- my_apmversionpath.stat.exists == false
- my_agentarchive.stat.exists == true
# And cleanup after ourselves
#- name: Removing downloaded files
# tags: install
# win_file: path={{apm_work_path}} state=absent
# when:
# - my_apmversionpath.stat.exists == false
# - my_agentarchive.stat.exists == true
Relevant Section
###############################################################
##### THIS SECTION ############################################
# Get the extract directory
- name: Locate the Extracted Directory
tags: install
win_shell: for /d %d in (*apm-agent*) do echo %d
args:
executable: cmd
chdir: "{{apm_work_path}}"
register: extout
###### UNABLE TO SEE RESULTS HERE ############
- debug: msg={{ item }}
with_items: extout.stdout_lines
I can see the values of my register
changed: [ciwsdbxd8559.silver.com] => {
"changed": true,
"cmd": "for /d %d in (*apm-agent*) do echo %d",
"delta": "0:00:00.062173",
"end": "2016-11-06 07:56:51.816995",
"invocation": {
"module_name": "win_shell"
},
"rc": 0,
"start": "2016-11-06 07:56:51.754822",
"stderr": "",
"stdout": "\r\nE:\\SA\\tmp\\apm>echo apm-agent-10.3.0.15-base-win-master-541edbf478cc5e960c33e90b394a9b9cd822def3 \r\napm-agent-10.3.0.15-base-win-master-541edbf478cc5e960c33e90b394a9b9cd822def3\r\n",
"stdout_lines": [
"",
"E:\\SA\\tmp\\apm>echo apm-agent-10.3.0.15-base-win-master-541edbf478cc5e960c33e90b394a9b9cd822def3 ",
"apm-agent-10.3.0.15-base-win-master-541edbf478cc5e960c33e90b394a9b9cd822def3"
],
"warnings": []
}
However the following debug command gives me the following:
TASK [../roles/apm_windows : debug] ********************************************
task path: /home/SILVER/c53259/ansible/apm_deployment/roles/apm_windows/tasks/agent.yml:95
ok: [ciwsdbxd8559.silver.com] => (item=extout.stdout) => {
"invocation": {
"module_args": {
"msg": "extout.stdout"
},
"module_name": "debug"
},
"item": "extout.stdout",
"msg": "extout.stdout"
}
Any thoughts on what is going on?
Using Ansible 2.2
Python 2.7
Are you sure this works without an error? Because mesg is not a valid option in debug. Did you mean msg?
- debug: mesg={{ item }}
Anyway, the problem is you are using a wrong parameter. Use var instead since you are printing the value of a variable.
- debug: var={{ item }}
with_items: extout.stdout_lines
Related
My ansible project consists of 6 roles (app, db, init, ror, tick, web). Each role represents the installation of one component of the system. Each device keeps track of which roles have been installed using the file "/etc/ansible/ansible-install-state.yml". It's initial contents are:
# ansible-install-state.yml
#
#
# This file is maintained by Ansible
# Please leave it alone, or accept the consequences!
#
roles:
app:
installed: false
db:
installed: false
init:
installed: false
ror:
installed: false
tick:
installed: false
web:
installed: false
At the completion of the 'init' role, I want to process the file to update the installed state to 'true'. I do that with the following call. (For testing, I'm also setting 'ror: true' and adding a new role 'new: unknown'
- name: System Init | Process ansible_install_state
ansible.builtin.include_tasks: process_ansible_install_state.yml
with_items:
- role: { key: init, value: true }
- role: { key: ror, value: true }
- role: { key: new, value: unknown}
Updating existing roles went fairly smoothly, but adding a new role did not. I finally got it to work by first adding the role itself, essentially 'new: {}', then modifying it again to add the 'installed: unknown' part. Although this does work, I can't help but believe there's a better way - a way to do it all at once. Following is the process_enable_install_state.yml file.
---
# process_ansible_install_state.yml
#
#------------------------- Process Ansible Install State File
#
- name: display variable
debug:
var: item
# See https://stackoverflow.com/questions/32994002/is-there-a-yaml-editing-module-for-ansible
- name: Ansible Install State | Read ansible-install-state.yml
ansible.builtin.slurp:
path: "/etc/ansible/ansible-install-state.yml"
register: install_state
- name: Ansible Install State | Decode and save yaml as fact
ansible.builtin.set_fact:
my_roles: "{{ install_state['content'] | b64decode | from_yaml }}"
register: its_a_fact
- name: Ansible Install State | Save the new ROLE block
block:
- name: Ansible Install State | Save the new role
ansible.utils.update_fact:
updates:
- path: my_roles.roles.{{ item.role.key }}
value: {}
register: updated
# Update_fact does not update 'in place', so we need to save it as a fact
- name: Ansible Install State | Update the saved 'fact'
ansible.builtin.set_fact:
my_roles: "{{ updated.my_roles}}"
when: my_roles.roles[item.role.key] is undefined
- name: debug
debug:
var: updated
- name: Ansible Install State | Update the role's installed state
ansible.utils.update_fact:
updates:
- path: my_roles.roles.{{ item.role.key }}.installed
value: "{{item.role.value}}"
register: updated
- name: debug
debug:
var: updated
# Update_fact does not update 'in place', so we need to save it as a fact
- name: Ansible Install State | Save modified as 'fact'
ansible.builtin.set_fact:
my_roles: "{{ updated.my_roles}}"
- name: Ansible Install State | Write Yaml File
ansible.builtin.copy:
content: '{{ my_roles | to_nice_yaml }}'
dest: "/etc/ansible/ansible-install-state.yml"
- name: Ansible Install State | Insert Header Comments
ansible.builtin.blockinfile:
path: "/etc/ansible/ansible-install-state.yml"
insertbefore: BOF
marker: "#{mark}"
marker_begin: "-------------------------------------------"
marker_end: "-------------------------------------------"
block: |
# ansible-install-state.yml
#
#
# This file is maintained by Ansible
# Please leave it alone, or accept the consequences!
#
#
This successfully produced...
# ansible-install-state.yml
#
#
# This file is maintained by Ansible
# Please leave it alone, or accept the consequences!
#
#
#-------------------------------------------
roles:
app:
installed: false
db:
installed: false
init:
installed: true
new:
installed: unknown
ror:
installed: true
tick:
installed: false
web:
installed: false
deploy#rpi-tick2:/etc/ansible $
I have a gitlab-runner running in openshift. I am running a playbook using this image registry.redhat.io/ansible-tower-38/ansible-runner-rhel7
The gitlab ci file:
image:
name: registry.redhat.io/ansible-tower-38/ansible-runner-rhel7
variables:
FF_GITLAB_REGISTRY_HELPER_IMAGE: "true"
stages:
- deploy
run-playbook:
tags:
- test
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
script:
- sh add_user_to_etc.sh
- export ANSIBLE_CONFIG="/builds/group-name/repo-name/ansible.cfg"
- echo "$ANSIBLE_CONFIG"
- ansible-playbook playbooks/linux-node.yml -e "ansible_user=$ROBOT_USERNAME ansible_ssh_pass=$ROBOT_PASSWORD"
The play file:
---
- name: Add Linux servers
hosts: all
gather_facts: yes
any_errors_fatal: true
environment:
http_proxy: "{{ proxy_global }}"
https_proxy: "{{ proxy_global }}"
no_proxy: "127.0.0.1"
roles:
- linux-node
The task where the error occurs:
- name: Build a JSON dashboard def
template:
src: templates/dashboard_summary.j2
dest: /tmp/{{ BLAH }}_dashboard_summary.json
force: yes
delegate_to: localhost
run_once: true
The error:
FAILED! => {"msg": "Failed to get information on remote file (/tmp/my-service-dev_dashboard_summary.json): sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted\nsudo: no valid sudoers sources found, quitting\nsudo: setresuid() [0, 0, 0] -> [1001010000, -1, -1]: Operation not permitted\nsudo: unable to initialize policy plugin\n"}
I'm guessing its because of the user 1001010000 openshift assigns. But other tasks are run using the robot user which has sudo permissions, those are all working fine. I don't know why this task alone uses 1001010000. Or maybe i got it all wrong.
Set up module in ansible gives an error when i tried to set custom facts on host machine using control machine
---
- hosts: test-servers
gather_facts: false
tasks:
- name: deleting Facts directory
file:
path: /etc/ansible/facts.d/
state: absent
- name: Creates a directiory
file:
path: /etc/ansible/facts.d/
recurse: yes
state: directory
- name: Copy custom date facts to host machine
copy:
src: /app/ansible_poc/roles/custom_facts/templates/facts.d/getdate.fact
dest: /etc/ansible/facts.d/getdate.fact
mode: 0755
- name: Copy custom role facts to host machine
copy:
src: /app/ansible_poc/roles/custom_facts/templates/facts.d/getrole.fact
dest: /etc/ansible/facts.d/getrole.fact
mode: 0755
- name: Reloading facts
setup:
- name: Display message
debug:
msg: "{{ ansible_local.getdate.date.date }}"
- name: Display message
debug:
msg: "{{ ansible_local.getrole.role.role }}"
I get following error when i tried to collect facts of ansible host machine. I have set up a file getdate.fact and getrole.fact which has code respectively
#############getdate.fact###############
echo [date]
echo date= `date`
########################################
#############getrole.fact###############
echo [role]
echo role= `whoami`
########################################
and when i tried to run the playbook main.yml then it following error.
[root#ansibletower tasks]# ansible -m setup test-servers
192.168.111.28 | FAILED! => {
"changed": false,
"cmd": "/etc/ansible/facts.d/getdate.fact",
"msg": "[Errno 8] Exec format error",
"rc": 8
}
192.168.111.27 | FAILED! => {
"changed": false,
"cmd": "/etc/ansible/facts.d/getdate.fact",
"msg": "[Errno 8] Exec format error",
"rc": 8
}
If I recall correctly, executables are expected to return JSON:
#!/bin/bash
echo '{ "date" : "'$( date )'" }'
You probably need to add "shebang" line to your fact scripts. I.e., getdate.fact should look like:
#!/bin/sh
echo [date]
echo date=`date`
Looking to create a list of directories for ansible to verify exist then check they are correctly defined and if not update them.
How can create multiple loops that work together?
# Determine if a path exists and is a directory.
- name: check directory existance and characteristics
stat: path=/path1
register: p1
# both that p.stat.isdir actually exists, and also that it's set to true.
- debug: msg="Path exists"
when: p1.stat.isdir is defined
- debug: msg="This is a directory"
when: p1.stat.isdir
- file: path=/path1 owner='user1' group='group1' mode=0755 state=directory
when: p1.stat.pw_name != 'user1' or p1.stat.gr_name != 'group1' or p1.stat.mode != '0755'
Ideally want to check all directories exist and update where they do then fail with list of those that don't.
Ultimately want something like file of directories and ownership settings to verify.
You can define a task.yml with below checks and then in a playbook execute the task with array of paths you need to run this task on.
----Task.yml
# Determine if a path exists and is a directory.
- name: check directory existance and characteristics
stat: path=/path1
register: p1
# both that p.stat.isdir actually exists, and also that it's set to true.
- debug: msg="Path exists"
when: p1.stat.isdir is defined
- debug: msg="This is a directory"
when: p1.stat.isdir
- file: path=/path1 owner='user1' group='group1' mode=0755 state=directory
when: p1.stat.pw_name != 'user1' or p1.stat.gr_name != 'group1' or p1.stat.mode != '0755'
playbook to run the task
--- Playbook
- name: Running Task
host: local
var:
- paths: ["path1" , "path2", "path3"]
tasks:
- include: task.yml path={{item}}
with_items: paths
Ideally want to check all directories exist and update where they do then fail with list of those that don't.
If that's the case then you're over-engineering things. All you need to do is invoke the file task with the appropriate parameters for each directory. Since Ansible is idempotent it will check the parameters for you and only change those which need to be changed. So something like this should be all you need:
- name: directories
file: path={{ item.p }}
state=directory
owner={{ item.o }}
group={{ item.g }}
mode=0755
with_items:
- { p: '/deploy', o: 'deploy_user', g: 'deploy_group' }
- { p: '/deploy/scripts', o: 'deploy_user', g: 'deploy_group' }
- { p: '/deploy/lib', o: 'deploy_user', g: 'deploy_group' }
The first time this task is run it will create the directories /deploy, /deploy/scripts, and /deploy/lib with the specified ownership & groups. The second time this task is run it should do nothing since those paths will already exist with the specified ownership & groups. Ansible will format the output nicely, especially if it's running in a shell with color enabled, so it will be easy to just read the output of this one task to determine what was changed and what wasn't.
Edit: If you want to test & display errors if directories don't exist then a simple two step approach should also work:
vars:
my_dirs:
- { p: '/deploy', o: 'deploy_user', g: 'deploy_group' }
- { p: '/deploy/scripts', o: 'deploy_user', g: 'deploy_group' }
- { p: '/deploy/lib', o: 'deploy_user', g: 'deploy_group' }
tasks:
- name: Check directories
stat: path={{ item.p }}
register: st
with_items: my_dirs
- name: Complain
debug: "Path {{ item.p }} does not exist or isn't set properly"
when: p1.stat.isdir is not defined or not p1.stat.isdir or p1.stat.pw_name != item.o or ...
with_items: my_dirs
- name: create directories
file: path={{ item.p }}
state=directory
owner={{ item.o }}
group={{ item.g }}
mode=0755
with_items: my_dirs
I have to run an ansible playbook to execute the following tasks
1) Calculate date in YYYY_MM_DD format and then use this prefix to download some file from aws to my local machine. The filename is of the following format 2015_06_04_latest_file.csv
2) I have to then create a folder by the name 2015_06_04 into multiple hosts and upload this file there.
This is my current playbook -
---
- hosts: 127.0.0.1
connection: local
sudo: yes
gather_facts: no
tasks:
- name: calculate date
shell: date "+%Y_%m_%d" --date="1 days ago"
register: output
- name: set date variable
set_fact: latest_date={{ item }}
with_items: output.stdout_lines
- local_action: command mkdir -p /tmp/latest_contracts/{{ latest_date }}
- local_action: command /root/bin/aws s3 cp s3://primarydatafolder/data/{{ latest_date }}_latest_data.csv /tmp/latest_contracts/{{ latest_date }}/ creates=/tmp/latest_contracts/{{ latest_date }}/latest_data.csv
register: result
ignore_errors: true
- local_action: command /root/bin/aws s3 cp s3://secondarydatafolder/data/{{ latest_date }}_latest_data.csv /tmp/latest_contracts/{{ latest_date }}/ creates=/tmp/latest_contracts/{{ latest_date }}/latest_data.csv
when: result|failed
# remove the date prefix from the downloaded file
- local_action: command ./rename_date.sh {{ latest_date }}
ignore_errors: true
- hosts: contractsServers
sudo: yes
gather_facts: no
tasks:
- name: create directory
file: path={{item.path}} state=directory mode=0775 owner=root group=root
with_items:
- {path: '/var/mukul/contracts/{{ latest_date }}' }
- {path: '/var/mukul/contracts/dummy' }
- name: copy dummy contracts
copy: src=dummy dest=/var/mukul/contracts/
- name: delete previous symlink
shell: unlink /var/mukul/contracts/latest
ignore_errors: true
- name: upload the newly created latest date folder to the host
copy: src=/tmp/latest_contracts/{{ latest_date }} dest=/var/mukul/contracts/
- name: create a symbolic link to the folder on the host and call it latest
action: file state=link src=/var/mukul/contracts/{{ latest_date }} dest=/var/mukul/contracts/latest
As per ansible's documentation on set_fact variable, this variable latest_date should be available across plays. However, ansible fails with the following message
failed: [192.168.101.177] => (item={'path': u'/var/mukul/contracts/{# latest_date #}'}) => {"failed": true, "item": {"path": "/var/mukul/contracts/{# latest_date #}"}}
msg: this module requires key=value arguments (['path=/var/mukul/contracts/{#', 'latest_date', '#}', 'state=directory', 'mode=0775', 'owner=root', 'group=root'])
It looks as if the second playbook is unable to get the value of the latest_date fact. Can you please tell me where i'm making a mistake?
Facts are host specific. As the documentation about set_fact says, "[v]ariables [set with set_fact] are set on a host-by-host basis".
Instead, I'd try using run_once as defined in Delegation, rolling updates, and local actions, like this:
- hosts: contractsServers
tasks:
- name: Determine date
local_action: shell: date "+%Y_%m_%d" --date="1 days ago"
register: yesterday
always_run: True
changed_when: False
run_once: True
- name: Do something else locally
local_action: ...
register: some_variable_name
always_run: True
changed_when: False
run_once: True
- name: Do something remotely using the variables registered above
...
You could enable fact-caching. You will need to set up a local redis instance where facts then will be stored.