tower_credential: expect ssh_key_data to be a string instead of path (#45158)

* expect ssh_key_data to be a string instead of path

ssh_key_data should be a string filled with the private key
the old behavior can be archived with a lookup

Fixes #45119

* clarifies ssh_key_data description, adds newline
This commit is contained in:
Meecr0b 2018-09-07 19:41:09 +02:00 committed by Sam Doran
parent ac4e611f9d
commit 2f6b8591b1
4 changed files with 81 additions and 44 deletions

View file

@ -0,0 +1,3 @@
---
minor_changes:
- tower_credential - Expect ssh_key_data to be the content of a ssh_key file instead of the path to the file (https://github.com/ansible/ansible/pull/45158)

View file

@ -51,6 +51,9 @@ The following modules will be removed in Ansible 2.12. Please update your playbo
Noteworthy module changes Noteworthy module changes
------------------------- -------------------------
* The ``tower_credential`` module originally required the ``ssh_key_data`` to be the path to a ssh_key_file.
In order to work like Tower/AWX, ``ssh_key_data`` now contains the content of the file.
The previous behavior can be achieved with ``lookup('file', '/path/to/file')``.
Plugins Plugins
======= =======

View file

@ -58,7 +58,8 @@ options:
- Password for this credential. Use ASK for prompting. secret_key for AWS. api_key for RAX. - Password for this credential. Use ASK for prompting. secret_key for AWS. api_key for RAX.
ssh_key_data: ssh_key_data:
description: description:
- Path to SSH private key. - SSH private key content. To extract the content from a file path, use the lookup function (see examples).
required: False
ssh_key_unlock: ssh_key_unlock:
description: description:
- Unlock password for ssh_key. Use ASK for prompting. - Unlock password for ssh_key. Use ASK for prompting.
@ -123,6 +124,17 @@ EXAMPLES = '''
organization: test-org organization: test-org
state: present state: present
tower_config_file: "~/tower_cli.cfg" tower_config_file: "~/tower_cli.cfg"
- name: Create a valid SCM credential from a private_key file
tower_credential:
name: SCM Credential
organization: Default
state: present
kind: scm
username: joe
password: secret
ssh_key_data: "{{ lookup('file', '/tmp/id_rsa') }}"
ssh_key_unlock: "passphrase"
''' '''
import os import os
@ -187,7 +199,7 @@ def main():
host=dict(), host=dict(),
username=dict(), username=dict(),
password=dict(no_log=True), password=dict(no_log=True),
ssh_key_data=dict(no_log=True, type='path'), ssh_key_data=dict(no_log=True, type='str'),
ssh_key_unlock=dict(no_log=True), ssh_key_unlock=dict(no_log=True),
authorize=dict(type='bool', default=False), authorize=dict(type='bool', default=False),
authorize_password=dict(no_log=True), authorize_password=dict(no_log=True),
@ -254,13 +266,18 @@ def main():
params['team'] = team['id'] params['team'] = team['id']
if module.params.get('ssh_key_data'): if module.params.get('ssh_key_data'):
filename = module.params.get('ssh_key_data') data = module.params.get('ssh_key_data')
if not os.path.exists(filename): if os.path.exists(data):
module.fail_json(msg='file not found: %s' % filename) module.deprecate(
if os.path.isdir(filename): msg='ssh_key_data should be a string, not a path to a file. Use lookup(\'file\', \'/path/to/file\') instead',
module.fail_json(msg='attempted to read contents of directory: %s' % filename) version="2.12"
with open(filename, 'rb') as f: )
module.params['ssh_key_data'] = to_text(f.read()) if os.path.isdir(data):
module.fail_json(msg='attempted to read contents of directory: %s' % data)
with open(data, 'rb') as f:
module.params['ssh_key_data'] = to_text(f.read())
else:
module.params['ssh_key_data'] = data
for key in ('authorize', 'authorize_password', 'client', for key in ('authorize', 'authorize_password', 'client',
'security_token', 'secret', 'tenant', 'subscription', 'security_token', 'secret', 'tenant', 'subscription',

View file

@ -5,6 +5,10 @@
- name: Generate a local SSH key - name: Generate a local SSH key
local_action: "shell ssh-keygen -b 2048 -t rsa -f {{ tempdir.stdout }}/id_rsa -q -N 'passphrase'" local_action: "shell ssh-keygen -b 2048 -t rsa -f {{ tempdir.stdout }}/id_rsa -q -N 'passphrase'"
- name: Read the generated key
set_fact:
ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}"
- name: Create a User-specific credential - name: Create a User-specific credential
tower_credential: tower_credential:
name: SSH Credential name: SSH Credential
@ -43,6 +47,46 @@
become_method: sudo become_method: sudo
become_username: superuser become_username: superuser
become_password: supersecret become_password: supersecret
ssh_key_data: "{{ ssh_key_data }}"
ssh_key_unlock: "passphrase"
register: result
- assert:
that:
- "result is changed"
- name: Create a valid SSH credential from lookup source
tower_credential:
name: SSH Credential from lookup source
organization: Default
state: present
kind: ssh
description: An example SSH credential from lookup source
username: joe
password: secret
become_method: sudo
become_username: superuser
become_password: supersecret
ssh_key_data: "{{ lookup('file', tempdir.stdout + '/id_rsa') }}"
ssh_key_unlock: "passphrase"
register: result
- assert:
that:
- "result is changed"
- name: Create a valid SSH credential from file source
tower_credential:
name: SSH Credential from file source
organization: Default
state: present
kind: ssh
description: An example SSH credential from file source
username: joe
password: secret
become_method: sudo
become_username: superuser
become_password: supersecret
ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_data: "{{ tempdir.stdout }}/id_rsa"
ssh_key_unlock: "passphrase" ssh_key_unlock: "passphrase"
register: result register: result
@ -50,6 +94,8 @@
- assert: - assert:
that: that:
- "result is changed" - "result is changed"
- "result is not failed"
- "'ssh_key_data should be a string, not a path to a file.' in result.deprecations[0].msg"
- name: Create an invalid SSH credential (passphrase required) - name: Create an invalid SSH credential (passphrase required)
tower_credential: tower_credential:
@ -58,7 +104,7 @@
state: present state: present
kind: ssh kind: ssh
username: joe username: joe
ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_data: "{{ ssh_key_data }}"
ignore_errors: yes ignore_errors: yes
register: result register: result
@ -67,38 +113,6 @@
- "result is failed" - "result is failed"
- "'must be set when SSH key is encrypted' in result.module_stderr" - "'must be set when SSH key is encrypted' in result.module_stderr"
- name: Create an invalid SSH credential (ssh_key_data file is missing)
tower_credential:
name: SSH Credential
organization: Default
state: present
kind: ssh
username: joe
ssh_key_data: "{{ tempdir.stdout }}/not_a_valid_file"
ignore_errors: yes
register: result
- assert:
that:
- "result is failed"
- "'file not found' in result.msg"
- name: Create an invalid SSH credential (ssh_key_data is a directory)
tower_credential:
name: SSH Credential
organization: Default
state: present
kind: ssh
username: joe
ssh_key_data: "{{ tempdir.stdout }}"
ignore_errors: yes
register: result
- assert:
that:
- "result is failed"
- "'attempted to read contents of directory' in result.msg"
- name: Create an invalid SSH credential (Organization not found) - name: Create an invalid SSH credential (Organization not found)
tower_credential: tower_credential:
name: SSH Credential name: SSH Credential
@ -202,7 +216,7 @@
kind: scm kind: scm
username: joe username: joe
password: secret password: secret
ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_data: "{{ ssh_key_data }}"
ssh_key_unlock: "passphrase" ssh_key_unlock: "passphrase"
register: result register: result
@ -338,7 +352,7 @@
kind: gce kind: gce
username: joe username: joe
project: ABC123 project: ABC123
ssh_key_data: "{{ tempdir.stdout }}/id_rsa" ssh_key_data: "{{ ssh_key_data }}"
register: result register: result
- assert: - assert: