How do I use ansible to create IAM users as shown in the documentation? - ansible

Setup
I want to use Ansible to configure my IAM users, groups and permissions but I am having trouble even getting off the ground. I installed the development fork of Ansible (2.1.0) and attempted to run the simple play shown in the example in the docs.
site.yml
# Basic user creation example
tasks:
- name: Create two new IAM users with API keys
iam:
iam_type: user
name: "{{ item }}"
state: present
password: "{{ temp_pass }}"
access_key_state: create
with_items:
- jcleese
- mpython
I ran the play with the following command:
$ ansible-playbook site.yml
And received the following error:
Error
ERROR! playbooks must be a list of plays
The error appears to have been in '~/aws_kingdom/site.yml': line 2, column 1, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
# Basic user creation example
tasks:
^ here
I am going to plead ignorance on a lack of understanding of the anatomy of a playbook especially one that should effectively have no hosts since it only applies to creating users in the AWS IAM service.
References
http://docs.ansible.com/ansible/iam_module.html

You still need to tell Ansible what hosts it needs to run on, just that it needs to run locally.
So instead your site.yml file should look like:
- hosts: 127.0.0.1
connection: local
tasks:
# Basic user creation example
- name: Create two new IAM users with API keys
iam:
iam_type: user
name: "{{ item }}"
state: present
password: "{{ temp_pass }}"
access_key_state: create
with_items:
- jcleese
- mpython

I encountered the:
ERROR! playbooks must be a list of plays
error myself and after double checking everything couldn't find the error.
By accident I found that when I removed any trailing whitespace and/or newlines from my playbook.yml that it fixed the issue.
It's weird because I tried validating my YAML with a linter before encountering this fix so I can't understand why it worked.
Admittedly, I don't have much experience with YAML so there might be some rule that I don't understand that I'm missing.

Related

How is this way of writing ansible task named? Is it deprecated?

I have an old ansible repository that I forked some time ago (2 years+) where I had not much idea about ansible. There are a lot of tasks written in a form that I don't know if it is correct or if it is deprecated. They use the action: section of the task, and then they write what looks like standard ansible actions (in fact, ansible-link complains that I should use FQN for built in actions on those).
I tried searching but all the results that I find are not relevant, so I am asking here how is this module action usually referred to, and should I change it to plain yaml?
- name: Disallow password authentication
action: lineinfile dest=/etc/ssh/sshd_config regexp="^PasswordAuthentication" line="PasswordAuthentication no" state=present
notify: restart ssh
Q: "Should I change it to plain YAML?"
A: Yes. The YAML format is the best practice
- name: Disallow password authentication
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PasswordAuthentication"
line: "PasswordAuthentication no"
state: present
notify: restart ssh
To see details and examples about lineinfile run
shell> ansible-doc -t module lineinfile
Regarding your question
How is this module action usually referred to
you may have a look into Playbook Keywords
"For example, action in task can be substituted by the name of any Ansible module."
"The ‘action’ to execute for a task, it normally translates into a C(module) or action plugin.".
Regarding your question
Is it deprecated?
Since it is referenced in the actual documenation without a note it seems to be not.
Regarding your question
There are a lot of tasks written in a form that I don't know if it is correct
Since it is referenced in the actual documenation without a note it seems to be correct syntax.
Therefore it is possible to write tasks like
- name: Gather stored entitlement from repository service
action:
module: uri
url: "https://{{ REPOSITORY_URL }}/api/system/security/certificates"
method: GET
url_username: "{{ ansible_user }}"
url_password: "{{ ansible_password }}"
validate_certs: yes
return_content: yes
status_code: 200
body_format: json
check_mode: false
register: result
- name: Show result
debug:
msg: "{{ result.json }}"
check_mode: false
which in example gather installed certificates from a JFrog Artifactory repository service via REST API call, as well
- name: Gather stored entitlement from repository service
local_action:
module: uri
...
local_action
Same as action but also implies delegate_to: localhost
for Controlling where tasks run: delegation and local actions.
Nevertheless, the YAML format is the best practice. Therefore you should change it to plain YAML if possible.

ansible: customize which password manager to use

New to Ansible here. I've been adapting an Ansible playbook to automate the setup of a Raspberry Pi to use Pi-hole as described on this blog and as per this repo.
One change I've made is to use Bitwarden instead of 1Password as the secrets manager (see details below).
But after having done it, and thinking of offering a PR to the repo in question, I realized that my use of Bitwarden is just as hard-coded as the use of 1Password in the original (hardly an improvement).
Is there a way to make the choice of secret manager configurable in an Ansible playbook or setup? Or "call a different function" based on the value of a variable?
A desirable setup would be something along the lines of:
vars:
secret_manager: bitwarden
# one of {'1password', 'bitwarden', 'lastpass', 'ansible-vault',
# 'aws-secrets', 'azure-key-vault', ...}
tasks:
- name: ...
...
value: "{{ lookup(secret_manager, item_name, field=field_name) }}"
Details
Here is an example of using bitwarden instead of 1password in a playbook.yml:
New:
roles:
- role: ansible-modules-bitwarden
tasks:
- name: "test: get 'username' from the secrets manager"
debug:
- msg: "{{ lookup('bitwarden', 'pi-hole', field='username') }}"
(and note: if the field is a custom one, one must add custom_field=true whereas with
community.general.onepassword this is not needed).
Instead of:
tasks:
- name: "test: get 'username' from the secrets manager"
debug:
- msg: "{{ lookup('community.general.onepassword', 'pi-hole', field='username') }}"
Side note and for sake of completeness, in order to make this work I needed to install Bitwarden CLI as well as (one of) the ansible-modules-bitwarden, and then of course log in and get a session token:
export BW_SESSION="$(bw unlock --raw)"
This all works fine, but I am not quite happy: now the use of Bitwarden is hard-coded instead of "1Password". I would like to make this more generic and customizable. What if someone would like to use LastPass or AWS Secrets Manager instead?
Things I've tried
using if-else:
- hosts: all
vars:
use_bitwarden: true
secret_item: pi-hole
roles:
- role: ansible-modules-bitwarden
when: use_bitwarden
tasks:
- name: get username from the secrets manager
debug:
msg: "{{ lookup('bitwarden', secret_item, field='username') if use_bitwarden \
else lookup('community.general.onepassword', secret_item, field='username') }}"
but this bloats the playbook, makes it less readable and less DRY. Also, it isn't very extensible to the many other secrets managers out there.
define which manager to use in a variable:
- hosts: all
vars:
secret_manager: bitwarden
# secret_manager: community.general.onepassword
secret_item: pi-hole
roles:
- role: ansible-modules-bitwarden
when: secret_manager == 'bitwarden'
tasks:
- name: get username from the secrets manager
debug:
msg: "{{ lookup(secret_manager, secret_item, field='username') }}"
but this doesn't work as custom fields are to be queried with custom_field=true with the Bitwarden plugin, but not with 1Password.
Other ideas (not tried)
define a new generic plugin that uses other existing ones, based on a config variable.
use a task to get all the needed variables from whatever password manager at once and insert them, encrypted, into Ansible's vault. That way the task that does that can be isolated and have conditional execution with when: secret_manager == 'that'. But it makes maintenance of the playbook more difficult.
Maybe there is a much simpler and concise way of accomplishing this?

Ansible - vars are not correctly propagated to handlers when role run in loop

I am asking for help with a problem of deploying multiple versions (different variables) of the app with the same role run from one playbook.
We have an app with multiple product families, which are different code versions. Each version has separate uWSGI vassal config and Nginx virtualhost config(/api/v2, /api/v3, ...).
The desired state would be to run playbook and configure the server with all versions specified.
Sadly, ansible's import_role/import_tasks can't be used with with_items, so include_role/include_tasks must be used (pitty because they do not honor role tags).
The include_role method would not be the biggest problem, but we use handlers to notify uWSGI touch to reload - on a code change, link change, virtualenv change, app_config change, ...).
But when using loop (with_items), the variables passed from the loop does not correctly propagate to handlers.
I tried this scenarios
playbook.yml - with_items loop inside the playbook
PROBLEM: Handler is run only for the first iteration of the loop.
#!/usr/bin/env ansible-playbook
# HAndler is run only once, from first notifier
- hosts: localhost
gather_facts: no
vars:
app_root: "/tmp/test_ansible"
app_versions:
- app_product_family: 1
app_release: "v1.0.2"
- app_product_family: 3
app_release: "v4.0.7"
tasks:
- name: Deploy multiple versions of app
include_role:
name: app
with_items: "{{ app_versions }}"
loop_control:
loop_var: app_version
vars:
app_product_family: "{{ app_version.app_product_family }}"
app_release: "{{ app_version.app_release }}"
tags:
- app
- app_debug
playbook_v2.yml - with_items loop inside role task
PROBLEM: Handler is run with the default value from "Defaults"
#!/usr/bin/env ansible-playbook
- hosts: localhost
gather_facts: no
roles:
- app_v2
vars:
app_v2_root: "/tmp/test_ansible_v2"
app_v2_versions:
- app_v2_product_family: 1
app_v2_release: "v1.0.2"
- app_v2_product_family: 3
app_v2_release: "v4.0.7"
Tasks roles/app_v2/main.yml
---
# Workaround because import_tasks can't be run with_items
- include_tasks: deploy.yml
when: app_v2_versions
with_items: "{{ app_v2_versions }}"
loop_control:
loop_var: app_v2
vars:
app_v2_product_family: "{{ app_v2.app_v2_product_family }}"
app_v2_release: "{{ app_v2.app_v2_release }}"
tags:
- app_v2
- app_v2_deploy
...
One idea was about writing a separate role for each product family, but they share nginx and uWSGI, so it will be lots of copy-pasting and sharing tasks (so tags would not work properly).
For now, I solved it with shell script wrapper, but this is not an ideal solution and does not work from Ansible tower.
Sample repo with tasks to reproduce problem (tested with ansible 2.4, 2.5, 2.6)
Any ideas & recommendations are very welcome.
The order of overrides for variables is broken for includes in Ansible. F.e. even set_fact in the included role will be shadowed by role defaults.
See this bug: https://github.com/ansible/ansible/issues/22025
It's closed but not fixed. My advice: use include and variables really carefully.
In practice I never use role includes with loop. If you need loop, include a tasklist in this loop (and that tasklist, in turn, may import_role).
Ok, It is a bug as #George Shuklin posted.
I will use my shell wrapper, which reads group_vars yaml and then runs the playbook multiple times according to the variable list length.
Sadly I hit multiple annoying bugs in ansible in last few weeks, kinda losing my trust in it ):
And probably everybody is using microservices and kubernetes, so need to speed up our migration (:

Ansible - Unable to run certain JUNOS modules

I'm trying to run the Ansible modules junos_cli and junos_rollback and I get the following error:
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.
The error appears to have been in '/home/quake/network-ansible/roles/junos-rollback/tasks/main.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- name: I've made a huge mistake
^ here
This is the role in question:
---
- name: I've made a huge mistake
junos_rollback:
host={{ inventory_hostname }}
user=ansible
comment={{ comment }}
confirm={{ confirm }}
rollback={{ rollback }}
logfile={{ playbook_dir }}/library/logs/rollback.log
diffs_file={{ playbook_dir }}/configs/{{ inventory_hostname }}
Here is the Juniper page:
http://junos-ansible-modules.readthedocs.io/en/1.3.1/junos_rollback.html
Their example's syntax is a little odd. host uses a colon while the rest uses = signs. I've tried mixing both and only using one or the other. I keep getting errors.
I also confirmed that my junos-eznc version is higher than 1.2.2 (I have 2.0.1)
I've been able to use junos_cli before, I don't know if a version mismatch happened. On the official Ansible documentation, there is no mention of junos_cli or junos_rollback. Perhaps they're not supported anymore?
http://docs.ansible.com/ansible/list_of_network_modules.html#junos
Thanks,
junos_cli & junos_rollback are part of Galaxy and not core modules. You can find them at
https://galaxy.ansible.com/Juniper/junos/
Is the content posted here has whole content of your playbook? if yes, You need to define other items too in your playbook such as roles, connection, local. For example
refer https://github.com/Juniper/ansible-junos-stdlib#example-playbook
```
---
- name: rollback example
hosts: all
roles:
- Juniper.junos
connection: local
gather_facts: no
tasks:
- name: I've made a huge mistake
junos_rollback:
host = {{inventory_hostname}}
----
----
```
Where have you saved the content of juniper.junos modules?. Can you post the content of your playbook and the output of the tree command to see your file structure? That could help.
I had a similar problem where Ansible was not finding my modules and what I did was to copy the juniper.junos folder to my roles folder and then added a tasks folder within it to execute the main.yaml from there.
Something like this:
/Users/macuared/Ansible_projects/roles/Juniper.junos/tasks
---
- name: "TEST 1 - Gather Facts"
junos_get_facts:
host: "{{ inventory_hostname}}"
user: "uuuuu"
passwd: "yyyyyy"
savedir: "/Users/macuared/Ansible_projects/Ouput/Facts"
ignore_errors: True
register: junos
- name: Checking Device Version
debug: msg="{{ junos.facts.serialnumber }}"
Additionally, I would add "" to the string values in your YAML. Something like this:
---
- name: I've made a huge mistake
junos_rollback:
host="{{ inventory_hostname }}"
user=ansible
comment="{{ comment }}"
confirm={{ confirm }}
rollback={{ rollback }}
logfile="{{ playbook_dir }}/library/logs/rollback.log"
diffs_file="{{ playbook_dir }}/configs/{{ inventory_hostname }}"
Regarding this "I've tried mixing both and only using one or the other. I keep getting errors."
I've used just colon and mine works fine even when in the documentation suggests = signs. See junos_get_facts

Ansible role_path undefined error

I have a task file that looks like this:
- name: Drop schemas
mysql_db: state=import name=mysql target={{ role_path }}/files/schemas/drop-imdb-perf.sql login_user={{ MYSQL_ROOT_USER }} login_password={{ MYSQL_ROOT_PWD }} login_host={{ inventory_hostname }}
I'm invoking it from a playbook that looks like this:
- name: Drop mySQL data
gather_facts: no
hosts: imdb
connection: local
tags:
- mysql-data-drop
tasks:
- include: ../roles/mysql/tasks/drop-perf.yml
I'm using Ansible version 1.9.4 so I'm thinking role_path should be a valid variable.
But when I run the playbook, I get this output:
TASK: [Drop schemas] **************************************************
fatal: [imdb] => One or more undefined variables: 'role_path' is undefined
I can't figure out why role_path is undefined. According to the ansible docs, it seems like for versions 1.8 and above it should be populated with the directory of the role in question, but I'm clearly mistaken about something or other.
I don't see you using any roles. Without looking into the Ansible code it seems obvious that role_path is defined within a role. Including a file of a role does not make it run in context of a role though.
If your include is by intend, role_path won't be defined. You could try to set it yourself together with the include like so:
tasks:
- include: ../roles/mysql/tasks/drop-perf.yml
role_path: ../roles/mysql
That might work or not, since role_path still is a magic variable and therefore might not be manually changed.
If you actually meant to include the role, then you need to define your playbook like this:
- name: Drop mySQL data
gather_facts: no
hosts: imdb
connection: local
tags:
- mysql-data-drop
roles:
- role: ../roles/mysql
But my guess is, you're trying to only run a single tasks file of that role and not the whole role. But what you're trying to do there seem to be against best practice. My recommendation would be to move the tag mysql-data-drop into the tasks of the file drop-perf.yml, because that's what tags are for: to trigger a limited set of tasks of roles or playbooks.

Resources