Creating IAM users with Ansible - getting the CLI credentials - ansible

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.

Related

Schedule deletion of unused template

In Ansible Tower, is there a possibility to create a scheduled task that checks if a template has not been executed for one year and if so, deletes it?
The short answers is: yes, of course. The long answer is: someone has to create such task. To do so, one may getting familiar with the Ansible Tower REST API, in detail Job Templates - List Jobs for a Job Template.
In example, a call for Jobs of a Job Template which was never executed
curl --silent --user ${ACCOUNT}:${PASSWORD} https://${TOWER_URL}/api/v2/job_templates/${ID}/jobs/ --write-out "\n%{http_code}\n"| jq .
would result into an output of
{
"count": 0,
"next": null,
"previous": null,
"results": []
}
200
A call for Jobs of a Job Template which is executed daily would result into an output of
{
"count": 70,
"next": "/api/v2/job_templates/<id>/jobs/?page=2",
"previous": null,
"results": [
{
"id": <id>,
<snip>
"created": "2022-06-10T05:57:18.976798Z",
"modified": "2022-06-10T05:57:19.666354Z",
"name": "<name>",
"description": "<description>",
"unified_job_template": <id>,
"launch_type": "manual",
"status": "successful",
"failed": false,
"started": "2022-06-10T05:57:19.870208Z",
"finished": "2022-06-10T05:57:33.752072Z",
"canceled_on": null,
"elapsed": 13.882,
"job_explanation": "",
"execution_node": "<executionNode>",
"controller_node": "",
"job_type": "run",
"inventory": <id>,
"project": <id>,
"playbook": "<path>",
"scm_branch": "",
"forks": 0,
"limit": "<hostgroup>",
"verbosity": 0,
"extra_vars": "{\"if_there_any\": \"false\"}",
"job_tags": "check",
"force_handlers": false,
"skip_tags": "",
"start_at_task": "",
"timeout": 0,
"use_fact_cache": false,
"organization": <id>,
"job_template": <id>,
"passwords_needed_to_start": [
"ssh_password"
],
"allow_simultaneous": false,
"artifacts": {},
"scm_revision": "<rev>",
"instance_group": 1,
"diff_mode": false,
"job_slice_number": 0,
"job_slice_count": 1,
"webhook_service": "",
"webhook_credential": null,
"webhook_guid": ""
}
]
}
200
Since the goal is to execute it via Ansible Engine, as well schedule via Ansible Tower, a sample rest.yml playbook
---
- hosts: localhost
become: false
gather_facts: false
vars:
TOWER_API_URL: "<tower_url>/api/v2"
FILTER: ".version"
ID: "<id>"
tasks:
- name: Example REST API call
shell:
cmd: curl --silent -u '{{ ansible_user }}:{{ ansible_password }}' --location {{ TOWER_API_URL }}/ping | jq {{ FILTER }}
warn: false
register: result
failed_when: result.rc != 0
changed_when: false
check_mode: false
- name: Show result
debug:
msg: "{{ result.stdout }}"
- name: List Jobs for a Job Template
uri:
url: "https://{{ TOWER_API_URL }}/job_templates/{{ ID }}/jobs/"
user: "{{ ansible_user }}"
password: "{{ ansible_password }}"
force_basic_auth: true
method: GET
validate_certs: yes
return_content: yes
status_code: 200
body_format: json
check_mode: false
register: result
- name: Show result
debug:
msg: "{{ result.json.results }}" # list of jobs
which can be called from CLI via
sshpass -p ${PASSWORD} ansible-playbook --user ${ACCOUNT} --ask-pass rest.yml
Please take note that the "count": 70 is greater than the result set result.json.results | length of 25 and there is a next page mentioned "next": "...?page=2". The result.json.results | last therefore does not contain the most recent execution. This is because of Pagination.
Depending on the setup and actual configuration of Ansible Tower one may need to adjust the page_size. In example to get the most recent result
...
url: "https://{{ TOWER_API_URL }}/job_templates/{{ ID }}/jobs/?page_size=100"
...
msg: "{{ result.json.results | last }}"

Juniper Software upgrade faills cause of not enough space

I've tried to update a EX2300 switch from Juniper with the ansible module(juniper_junos_software) but eveytime i tried it fails cause it doesn't have enough space and i tried a bunch of stuff all from trying to SCP with other Ansible modules such as (net_put and junipernetworks.junos.junos_scp) which to no relief i can't get to work either
The Ansible code is:
- name: Install Junos OS
hosts: EX
roles:
- Juniper.junos
connection: local
gather_facts: no
vars:
OS_version: "20.4R1.12"
OS_package: "junos-arm-32-20.4R1.12.tgz"
pkg_dir: "/etc/JunOS"
log_dir: "/var/log"
netconf_port: 830
wait_time: 3600
tasks:
- name: Checking NETCONF connectivity
wait_for:
host: "{{ inventory_hostname }}"
port: "{{ netconf_port }}"
timeout: 5
- name: Clean up the device
juniper_junos_command:
commands:
- request system snapshot delete snap*
- request system software delete jweb
timeout: 200
register: response
- name: Print response from Clean up the device
debug:
var: response
- name: Install Junos OS package
juniper_junos_software:
version: "{{ OS_version }}"
local_package: "{{ pkg_dir }}/{{ OS_package }}"
cleanfs: yes
validate: no
reboot: true
logfile: "{{ log_dir }}/ansible.log"
register: sw
notify:
- wait_reboot
- name: Print response
debug:
var: response
- name: Snapshot Slice alternate
juniper_junos_command:
commands: request system snapshot slice alternate
timeout: 200
register: response
handlers:
- name: wait_reboot
wait_for:
host: "{{ inventory_hostname }}"
port: "{{ netconf_port }}"
timeout: "{{ wait_time }}"
when: not sw.check_mode
The error i get is:
"changed": true,
"check_mode": false,
"invocation": {
"module_args": {
"attempts": null,
"baud": null,
"checksum": null,
"checksum_algorithm": "md5",
"checksum_timeout": 300,
"cleanfs_timeout": 300,
"console": null,
"cs_passwd": null,
"cs_user": null,
"force_host": false,
"host": "10.15.84.100",
"issu": false,
"level": null,
"logdir": null,
"logfile": "/var/log/ansible.log",
"mode": null,
"nssu": false,
"passwd": null,
"port": 830,
"provider": null,
"ssh_config": null,
"ssh_private_key_file": "/etc/ansible/ssh-keys/id_ed25519",
"timeout": 30,
"user": "ansible",
"validate": false,
"vmhost": false
}
},
"msg": [
"Unable to install the software %s",
"\nERROR: estimate of space required: 119 Mbytes, available: 41 Mbytes\n"
]
}
"space required: 119 Mbytes, available: 41 Mbytes" indicates you need to delete some files, usually image files.

Error in ovirt_disk module via ansible AWX

I'm getting below wired error when trying to add disk to RHVM. I have checked in the documentation. all parameters seems legit to me. I need extra eye to valiate this case. thank you in-advanced
error msg as follows
"msg": "Unsupported parameters for (ovirt_disk) module: activate Supported parameters include: auth, bootable, description, download_image_path, fetch_nested, force, format, id, image_provider, interface, logical_unit, name, nested_attributes, openstack_volume_type, poll_interval, profile, quota_id, shareable, size, sparse, sparsify, state, storage_domain, storage_domains, timeout, upload_image_path, vm_id, vm_name, wait"
}
Parameters in the role as follows
"module_args": {
"vm_name": "Jxyxyxyxy01",
"activate": true,
"storage_domain": "Data-xxx-Txxx",
"description": "Created using Jira ticket CR-329",
"format": "cow",
"auth": {
"timeout": 0,
"url": "https://xxxxxx.com/ovirt-engine/api",
"insecure": true,
"kerberos": false,
"compress": true,
"headers": null,
"token": "xxcddsvsdvdsvsdvdEFl0910KES84qL8Ff5NReA",
"ca_file": null
},
"state": "present",
"sparse": true,
"interface": "virtio_scsi",
"wait": true,
"size": "20GiB",
"name": "Jxyxyxyxy01_123"
}
Playbook as follows.
- name: Create New Disk size of {{ disk_size }} on {{ hostname }} using storage domain {{ vm_storage_domain }}
ovirt_disk:
auth: "{{ ovirt_auth }}"
description: "Created using Jira ticket {{ issueKey }}"
storage_domain: "{{ vm_storage_domain }}"
name: "{{ hostname }}_123" # name of the disk
vm_name: "{{ hostname }}" #name of the virtual machine
interface: "virtio_scsi"
size: "{{ disk_size }}GiB"
sparse: yes
format: cow
activate: yes
wait: yes
state: present
register: vm_disk_results
The activate parameter was added in ansible 2.8.
Upgrade your ansible installation or drop that parameter.

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 }}"

how to access ansible newly created dictionary

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.

Resources