how to access ansible newly created dictionary - ansible

I am following the example here http://docs.ansible.com/ansible/iam_policy_module.html to try to create a new AWS IAM user and then use this new user's username to attach an IAM policy to it.
The example:
task:
- name: Create Two Groups, Mario and Luigi
iam:
iam_type: group
name: "{{ item }}"
state: present
with_items:
- Mario
- Luigi
register: new_groups
- name: Apply READ-ONLY policy to new groups that have been recently created
iam_policy:
iam_type: group
iam_name: "{{ item.created_group.group_name }}"
policy_name: "READ-ONLY"
policy_document: readonlypolicy.json
state: present
with_items: "{{ new_groups.results }}"
I have adapted that to work with one user:
- hosts: 127.0.0.1
gather_facts: no
connection: local
tasks:
- name: Create user lamda_ecr_delete
iam:
iam_type: user
name: "{{ item }}"
state: present
with_items:
- lambda_ecr_delete
register: new_user
- name: Apply ecr delete policy to newly created user
iam_policy:
iam_type: user
iam_name: "{{ item.created_user.user_name }}"
policy_name: "lambda_ecr_delete"
policy_document: assets/aws-policies/lambda_ecr_delete.json
state: present
with_items: "{{ new_user.results }}"
But when I try to retrieve the username in the dictionary, item.created_user does not exist.
When I use debug to see the content of {{ new_user.results }} I can identify that it's a python list that contains a dict so I can probably access it with [0] and then call invocation.module_args.name which is a valid key.
This is the output for debug: msg="{{ new_user.results }}" when run with --check:
ok: [127.0.0.1] => {
"changed": false,
"msg": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"iam_type": "user",
"name": "lambda_ecr_delete",
"state": "present"
}
},
"item": "lambda_ecr_delete",
"msg": "remote module (iam) does not support check mode",
"skipped": true
}
]
}
But that seems hackish. Is there a shortcut to access those module_args directly? Something as shown in the example with a .created_user?

Use item.user_meta.created_user.user_name instead.
You could note that created_user is nested into user_meta if you inspect debug output of new_user.results. Looks like:
"user_meta": {
"access_keys": null,
"created_user": {
"arn": "arn:aws:iam::<yourid>:user/test-ansible",
"create_date": "2017-04-03T16:31:53.530Z",
"path": "/",
"user_id": "EXAMPLEKAJHFEXAMPLE",
"user_name": "test-ansible"
},
"password": null
}
But be warned that on the second run iam module returns different output:
"user_name": "test-ansible"
instead of user_meta dictionary.

Related

How do I work with Ansible list of dicts?

I'm so confused with this. If I have a file containing:
users:
- name: jconnor
first: john
last: connor
uid: 3003
- name: sconnor
first: sarah
last: connor
uid: 3001
How do I get the details of each user? With this simple playbook:
- name: create users
hosts: localhost
gather_facts: false
tasks:
- name: Include vars
include_vars:
file: user_list.yml
name: users
- name: debug
debug:
msg: "{{ item }}"
with_dict: "{{ users }}"
I get the following which I can't use:
ok: [localhost] => (item={'value': [{u'last': u'connor', u'uid': 3003, u'name': u'jconnor', u'first': u'john'}, {u'last': u'connor', u'uid': 3001, u'name': u'sconnor', u'first': u'sarah'}], 'key': u'users'}) => {
"msg": {
"key": "users",
"value": [
{
"first": "john",
"last": "connor",
"name": "jconnor",
"uid": 3003
},
{
"first": "sarah",
"last": "connor",
"name": "sconnor",
"uid": 3001
}
]
}
}
I want to create user accounts with this but I simply don't understand how to use this structure.
Note that this is part of a larger structure and I can't change it.
Thanks
Since the users variable is a list of dicts, you should loop with loop or with_items. Then we can access the key of each dict with item.key. E.g.: item.name, item.uid, etc.
Note that you are importing the variables from the file with the name users. So this variable now contains the users hash of that file. If you skip name: users in include_var, then you can directly access the users dict while looping.
tasks:
- include_vars:
file: user_list.yml
name: users
- debug:
msg: "Username is {{ item.name }}, full name is {{ item.first }} {{ item.last }}, userid is {{ item.uid }}"
with_items: "{{ users.users }}"
This outputs message (showing 1 item):
ok: [localhost] => (item={u'last': u'connor', u'uid': 3003, u'name': u'jconnor', u'first': u'john'}) => {
"msg": "Username is jconnor, full name is john connor, userid is 3003"
}

Creating IAM users with Ansible - getting the CLI credentials

I am create usernames with the iam module, and I am using the access_key_state: create option.
However, I want my playbook to output the Access Key and the Secret Access Key for each user.
playbook.yml:
---
- name: "Starting the tasks: Creates IAM Policy, group, Role and User"
hosts: localhost
connection: local
gather_facts: False
vars_files:
- vars/aws-credentials.yml
tasks:
- include: tasks/create-user.yml
tags: user
- include: tasks/create-group.yml
tags: group
tasks/create-user.yml:
---
# Create the IAM users with Console and API access
- name: Create new IAM users with API keys and console access
iam:
iam_type: user
name: "{{ item }}"
state: present
password: "{{ lookup('password', 'passwordfile chars=ascii_letters') }}"
access_key_state: create
update_password: on_create
no_log: true
register: newusers
loop:
- johna
- mariab
- carlosc
- name: test
debug:
msg: "{{ credentials.results }}"
The debug message "{{ credentials.results }}" gives me the Access Key, but not the Secret Access Key:
{
"ansible_loop_var": "item",
"changed": true,
"created_keys": [],
"failed": false,
"groups": null,
"invocation": {
"module_args": {
"access_key_ids": null,
"access_key_state": "create",
"aws_access_key": null,
"aws_secret_key": null,
"debug_botocore_endpoint_logs": false,
"ec2_url": null,
"groups": null,
"iam_type": "user",
"key_count": 1,
"name": "carol.v",
"new_name": null,
"new_path": null,
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"path": "/",
"profile": null,
"region": null,
"security_token": null,
"state": "present",
"trust_policy": null,
"trust_policy_filepath": null,
"update_password": "always",
"validate_certs": true
}
},
"item": "carlosc",
"keys": {
"AK_________FV": "Active"
},
"user_meta": {
"access_keys": [
{
"access_key_id": "AK_________FV",
"status": "Active"
}
]
},
"user_name": "carlosc"
}
How to get the Secret Access Key for each user?
Update 09 May 2020: For further reference.
Bad news; it appears they are purposefully throwing the secret_access_key in the trash: https://github.com/ansible/ansible/blob/v2.9.7/lib/ansible/modules/cloud/amazon/iam.py#L238-L241
It appears the only way around that is to set key_count: 0 in your iam: and then use awscli or a custom ansible module to make that same iam.create_access_key call and preserve the result
- name: create access key for {{ item }}
command: aws iam create-access-key --user-name {{ item }}
environment:
AWS_REGION: '{{ the_region_goes_here }}'
AWS_ACCESS_KEY_ID: '{{ whatever_you_called_your_access_key }}'
AWS_SECRET_ACCESS_KEY: '{{ your_aws_secret_access_key_name_here }}'
register: user_keys
with_items:
- johna
- mariab
- carlosc
Feel free to file an issue, although you'll likely have to file it against the new amazon.aws collection since that iam.py is no longer present in the devel branch
You can use community.aws.iam:
- name: Create IAM User with API keys
community.aws.iam:
iam_type: user
name: some_dummy_user
state: present
access_key_state: create
register: new_user
- debug:
var: new_user
You'll be able to get your access and secret keys at:
new_user.user_meta.access_keys[0].access_key_id
new_user.user_meta.access_keys[0].secret_access_key
I have mine getting loaded into Secrets Manager and will eventually have them rotated with a lambda function.

Ansible - using for loops inside user module

I recently discovered the use of for loops in Ansible and was very excited about it.
Tries to use it inside the debug module and it worked superfine, but when I am trying to use the same inside "user" module, the control flow is not able to identify the "name" keyword of user module. Below is my poetry,
- hosts: testservers
tasks:
- name: Setting user facts
set_fact:
username: "{{ lookup('ini', 'name section=userDetails file=details.ini') }}"
userpass: "{{ lookup('ini', 'password section=userDetails file=details.ini') }}"
- name: User creation
become: true
# debug:
# msg: |
# {% for x,y in item.1,item.2 %}
# {{ x }} is the username and its password is {{ y }}
# {% endfor %}
# with_items:
# - { 1: "{{ username.split(',') }}", 2: "{{ userpass.split(',') }}" }
user: |
{% for x,y in item.1,item.2 %}
name: "{{ x }}"
password: "{{ y }}"
{% endfor %}
with_items:
- { 1: "{{ username.split(',') }}", 2: "{{ userpass.split(',') }}" }
Details.ini file contents below
#User basic details
[userDetails]
name=vasanth,vasanthnagkv
password=vasanth12,pass2
The commented part above works fine. but the uncommented part throws the below error
failed: [10.0.0.47] (item={1: [u'vasanth', u'vasanthnagkv'], 2: [u'vasanth12', u'pass2']}) => {
"changed": false,
"invocation": {
"module_args": {
"append": false,
"create_home": true,
"force": false,
"move_home": false,
"non_unique": false,
"remove": false,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on APUA-02",
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"update_password": "always"
}
},
"item": {
"1": [
"vasanth",
"vasanthnagkv"
],
"2": [
"vasanth12",
"pass2"
]
},
"msg": "missing required arguments: name"
}
to retry, use: --limit #/home/admin/ansiblePlaybooks/userCreation/userCreate.retry
PLAY RECAP ************************************************************************************************************************************************************************************
10.0.0.47 : ok=2 changed=0 unreachable=0 failed=1
Appreciate any kind of help here.
This line user: | means your passing a string to user module using the block style indicator: https://yaml-multiline.info/)
Since Ansible will just treat it as a string, you are not passing the required name parameter to the user module
Try splitting the names after the lookup in the first task so you can have the names and passwords list:
- name: Setting user facts
set_fact:
username: "{{ lookup('ini', 'name section=userDetails file=details.ini').split(',') }}"
userpass: "{{ lookup('ini', 'password section=userDetails file=details.ini').split(',') }}"
Once you have both the username and password list, you can use both variables by:
- user:
name: "{{ item }}"
password: "{{ userpass[index] }}"
loop: "{{ username }}"
loop_control:
index_var: index

How to do multi-conditional loops with Ansible?

I have roles and users. I would like to loop over my roles for users that contains the state=present.
iam_roles:
- name: "developers-role"
assume_role_policy_document: "developers"
state: present
managed_policy:
- arn:aws:iam::XXXXXXXXXXX:policy/CustomAmazonS3ReadOnlyAccess
- name: "bigdata-role"
assume_role_policy_document: "bigdata"
state: present
managed_policy:
- arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
- arn:aws:iam::XXXXXXXXXXX:policy/CustomAmazonRDSReadOnlyAccess
iam_users:
- name: test-user-1
state: present
groups: [developers]
password:
slack_name:
access_key_state: create
- name: test-user-2
state: present
groups: [developers]
password:
slack_name:
I'm trying filter and get only the users with the state=present and use it on my when clause, but no luck so far.
- name: Loop all the present users
debug: msg={{ item }}
when: "{{ item.state == 'present' }}"
with_items: "{{ iam_users }}"
tags: always
register: present_users
- set_fact:
iam_present_users: "{{ present_users.results }}"
tags: always
- name: Show only present users, ideally
debug: msg="{{ iam_present_users }}"
tags: always
- name: Manage AWS IAM Roles
iam_role:
name: "{{ item.name }}"
assume_role_policy_document: "{{ lookup('template', policies_path + '/assume-role/' + item.assume_role_policy_document + '.json') }}"
state: "{{ item.state }}"
managed_policy: "{{ item.managed_policy }}"
when: "{{ item.managed_policy is defined and iam_present_users is defined }}"
with_items: "{{ iam_roles }}"
tags: manage_roles
Your use of a debug statement to try to extract users seems odd. If you want to select objects from a list based on the value of an attribute, your best choice is probably the Jinja2 selectattr filter. For example, given this input:
iam_users:
- name: test-user-1
state: present
groups: [developers]
password:
slack_name:
access_key_state: create
- name: test-user-2
state: present
groups: [developers]
password:
slack_name:
- name: test-user-3
state: absent
groups: [developers]
password:
slack_name:
You could use this set_fact task:
- set_fact:
iam_present_users: "{{ iam_users|selectattr('state', 'equalto', 'present')|list }}"
Which would result in iam_present_users containing:
"iam_present_users": [
{
"access_key_state": "create",
"groups": [
"developers"
],
"name": "test-user-1",
"password": null,
"slack_name": null,
"state": "present"
},
{
"groups": [
"developers"
],
"name": "test-user-2",
"password": null,
"slack_name": null,
"state": "present"
}
]
See the jinja documentation for the stock list of filters, and the ansible documentation for a list of filters specific to ansible.

ansible - copy key to authorized keys file

I have created a user using ansible and now would like to copy the .ssh/id_rsa.pub file to the authorized_keys file.
I checked the authorized_keys module but that is used to copy the keys from the host machine to the guest.
Wondering what would the right approach.
- name: Adding user - {{ user }}
user: name={{ user }}
group={{ group }}
shell=/bin/bash
password=${password}
groups=sudo
append=yes
generate_ssh_key=yes
ssh_key_bits=2048
ssh_key_file=.ssh/id_rsa
The generated key is returned by the user module, so you can register the result and then use the key in a subsequent authorized_key task. That is, if I have a playbook like this:
- hosts: localhost
tasks:
- name: add user
user:
name: testuser
shell: /bin/bash
password: secret
append: yes
generate_ssh_key: yes
ssh_key_bits: 2048
register: newuser
- debug:
var: newuser
I will see as output something like:
TASK [debug] *******************************************************************
ok: [localhost] => {
"newuser": {
"append": true,
"changed": true,
"comment": "",
"group": 21946,
"home": "/home/testuser",
"move_home": false,
"name": "testuser",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"ssh_fingerprint": "2048 SHA256:Tn6UOl/WYToJCaW3QUnLMWgEfthILIsoCP+534qWzfw ansible-generated on lkellogg-pc0dzzve (RSA)",
"ssh_key_file": "/home/testuser/.ssh/id_rsa",
"ssh_public_key": "ssh-rsa ... ansible-generated on examplehost",
"state": "present",
"uid": 21940
}
}
So you can add a task like this:
- authorized_key:
user: root
state: present
key: "{{ newuser.ssh_public_key }}"

Resources