@ -79,6 +79,14 @@ options:
default : " yes "
choices : [ " yes " , " no " ]
version_added : " 2.1 "
comment :
description :
- Change the comment on the public key . Rewriting the comment is useful in
cases such as fetching it from GitHub or GitLab .
- If no comment is specified , the existing comment will be kept .
required : false
default : None
version_added : " 2.4 "
author : " Ansible Core Team "
'''
@ -220,6 +228,7 @@ from ansible.module_utils.basic import AnsibleModule
from ansible . module_utils . pycompat24 import get_exception
from ansible . module_utils . urls import fetch_url
class keydict ( dict ) :
""" a dictionary that maintains the order of keys as they are added
@ -349,6 +358,7 @@ def keyfile(module, user, write=False, path=None, manage_dir=True):
return keysfile
def parseoptions ( module , options ) :
'''
reads a string containing ssh - key options
@ -369,6 +379,7 @@ def parseoptions(module, options):
return options_dict
def parsekey ( module , raw_key , rank = None ) :
'''
parses a key , which may or may not contain a list
@ -430,6 +441,7 @@ def parsekey(module, raw_key, rank=None):
return ( key , key_type , options , comment , rank )
def readfile ( filename ) :
if not os . path . isfile ( filename ) :
@ -441,6 +453,7 @@ def readfile(filename):
finally :
f . close ( )
def parsekeys ( module , lines ) :
keys = { }
for rank_index , line in enumerate ( lines . splitlines ( True ) ) :
@ -454,6 +467,7 @@ def parsekeys(module, lines):
keys [ line ] = ( line , ' skipped ' , None , None , rank_index )
return keys
def writefile ( module , filename , content ) :
fd , tmp_path = tempfile . mkstemp ( ' ' , ' tmp ' , os . path . dirname ( filename ) )
@ -467,6 +481,7 @@ def writefile(module, filename, content):
f . close ( )
module . atomic_move ( tmp_path , filename )
def serialize ( keys ) :
lines = [ ]
new_keys = keys . values ( )
@ -496,11 +511,12 @@ def serialize(keys):
key_line = key [ 0 ]
else :
key_line = " %s %s %s %s \n " % ( option_str , key_type , keyhash , comment )
except :
except Exception :
key_line = key
lines . append ( key_line )
return ' ' . join ( lines )
def enforce_state ( module , params ) :
"""
Add or remove key .
@ -513,6 +529,7 @@ def enforce_state(module, params):
state = params . get ( " state " , " present " )
key_options = params . get ( " key_options " , None )
exclusive = params . get ( " exclusive " , False )
comment = params . get ( " comment " , None )
error_msg = " Error getting key from: %s "
# if the key is a url, request it and use it as key source
@ -559,6 +576,9 @@ def enforce_state(module, params):
# rank here is the rank in the provided new keys, which may be unrelated to rank in existing_keys
parsed_new_key = ( parsed_new_key [ 0 ] , parsed_new_key [ 1 ] , parsed_options , parsed_new_key [ 3 ] , parsed_new_key [ 4 ] )
if comment is not None :
parsed_new_key = ( parsed_new_key [ 0 ] , parsed_new_key [ 1 ] , parsed_new_key [ 2 ] , comment , parsed_new_key [ 4 ] )
matched = False
non_matching_keys = [ ]
@ -607,23 +627,28 @@ def enforce_state(module, params):
if do_write :
filename = keyfile ( module , user , do_write , path , manage_dir )
new_content = serialize ( existing_keys )
diff = None
if module . _diff :
diff = {
' before_header ' : params [ ' keyfile ' ] ,
' after_header ' : filename ,
' before ' : existing_content ,
' after ' : new_content ,
}
params [ ' diff ' ] = diff
if module . check_mode :
module . exit_json ( changed = True , diff = diff )
writefile ( module , filename , new_content )
params [ ' changed ' ] = True
params [ ' diff ' ] = diff
else :
if module . check_mode :
module . exit_json ( changed = False )
return params
def main ( ) :
module = AnsibleModule (
argument_spec = dict (
@ -635,6 +660,7 @@ def main():
key_options = dict ( required = False , type = ' str ' ) ,
unique = dict ( default = False , type = ' bool ' ) ,
exclusive = dict ( default = False , type = ' bool ' ) ,
comment = dict ( required = False , default = None , type = ' str ' ) ,
validate_certs = dict ( default = True , type = ' bool ' ) ,
) ,
supports_check_mode = True
@ -643,5 +669,6 @@ def main():
results = enforce_state ( module , module . params )
module . exit_json ( * * results )
if __name__ == ' __main__ ' :
main ( )