1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# 4# (c) 2017, René Moser <mail@renemoser.net> 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import (absolute_import, division, print_function) 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14DOCUMENTATION = r''' 15--- 16module: vultr_user 17short_description: Manages users on Vultr. 18description: 19 - Create, update and remove users. 20version_added: "2.5" 21author: "René Moser (@resmo)" 22options: 23 name: 24 description: 25 - Name of the user 26 required: true 27 type: str 28 email: 29 description: 30 - Email of the user. 31 - Required if C(state=present). 32 type: str 33 password: 34 description: 35 - Password of the user. 36 - Only considered while creating a user or when C(force=yes). 37 type: str 38 force: 39 description: 40 - Password will only be changed with enforcement. 41 default: no 42 type: bool 43 api_enabled: 44 description: 45 - Whether the API is enabled or not. 46 default: yes 47 type: bool 48 acls: 49 description: 50 - List of ACLs this users should have, see U(https://www.vultr.com/api/#user_user_list). 51 - Required if C(state=present). 52 - One or more of the choices list, some depend on each other. 53 choices: 54 - manage_users 55 - subscriptions 56 - provisioning 57 - billing 58 - support 59 - abuse 60 - dns 61 - upgrade 62 aliases: [ acl ] 63 type: list 64 state: 65 description: 66 - State of the user. 67 default: present 68 choices: [ present, absent ] 69 type: str 70extends_documentation_fragment: vultr 71''' 72 73EXAMPLES = r''' 74- name: Ensure a user exists 75 local_action: 76 module: vultr_user 77 name: john 78 email: john.doe@example.com 79 password: s3cr3t 80 acls: 81 - upgrade 82 - dns 83 - manage_users 84 - subscriptions 85 - upgrade 86 87- name: Remove a user 88 local_action: 89 module: vultr_user 90 name: john 91 state: absent 92''' 93 94RETURN = r''' 95--- 96vultr_api: 97 description: Response from Vultr API with a few additions/modification 98 returned: success 99 type: complex 100 contains: 101 api_account: 102 description: Account used in the ini file to select the key 103 returned: success 104 type: str 105 sample: default 106 api_timeout: 107 description: Timeout used for the API requests 108 returned: success 109 type: int 110 sample: 60 111 api_retries: 112 description: Amount of max retries for the API requests 113 returned: success 114 type: int 115 sample: 5 116 api_retry_max_delay: 117 description: Exponential backoff delay in seconds between retries up to this max delay value. 118 returned: success 119 type: int 120 sample: 12 121 version_added: '2.9' 122 api_endpoint: 123 description: Endpoint used for the API requests 124 returned: success 125 type: str 126 sample: "https://api.vultr.com" 127vultr_user: 128 description: Response from Vultr API 129 returned: success 130 type: complex 131 contains: 132 id: 133 description: ID of the user. 134 returned: success 135 type: str 136 sample: 5904bc6ed9234 137 api_key: 138 description: API key of the user. 139 returned: only after resource was created 140 type: str 141 sample: 567E6K567E6K567E6K567E6K567E6K 142 name: 143 description: Name of the user. 144 returned: success 145 type: str 146 sample: john 147 email: 148 description: Email of the user. 149 returned: success 150 type: str 151 sample: "john@exmaple.com" 152 api_enabled: 153 description: Whether the API is enabled or not. 154 returned: success 155 type: bool 156 sample: true 157 acls: 158 description: List of ACLs of the user. 159 returned: success 160 type: list 161 sample: [manage_users, support, upgrade] 162''' 163 164from ansible.module_utils.basic import AnsibleModule 165from ansible.module_utils.vultr import ( 166 Vultr, 167 vultr_argument_spec, 168) 169 170 171ACLS = [ 172 'manage_users', 173 'subscriptions', 174 'provisioning', 175 'billing', 176 'support', 177 'abuse', 178 'dns', 179 'upgrade', 180] 181 182 183class AnsibleVultrUser(Vultr): 184 185 def __init__(self, module): 186 super(AnsibleVultrUser, self).__init__(module, "vultr_user") 187 188 self.returns = { 189 'USERID': dict(key='id'), 190 'name': dict(), 191 'email': dict(), 192 'api_enabled': dict(convert_to='bool'), 193 'acls': dict(), 194 'api_key': dict() 195 } 196 197 def _common_args(self): 198 return { 199 'name': self.module.params.get('name'), 200 'email': self.module.params.get('email'), 201 'acls': self.module.params.get('acls'), 202 'password': self.module.params.get('password'), 203 'api_enabled': self.get_yes_or_no('api_enabled'), 204 } 205 206 def get_user(self): 207 users = self.api_query(path="/v1/user/list") 208 for user in users or []: 209 if user.get('name') == self.module.params.get('name'): 210 return user 211 return {} 212 213 def present_user(self): 214 user = self.get_user() 215 if not user: 216 user = self._create_user(user) 217 else: 218 user = self._update_user(user) 219 return user 220 221 def _has_changed(self, user, data): 222 for k, v in data.items(): 223 if k not in user: 224 continue 225 elif isinstance(v, list): 226 for i in v: 227 if i not in user[k]: 228 return True 229 elif data[k] != user[k]: 230 return True 231 return False 232 233 def _create_user(self, user): 234 self.module.fail_on_missing_params(required_params=['password']) 235 236 self.result['changed'] = True 237 238 data = self._common_args() 239 self.result['diff']['before'] = {} 240 self.result['diff']['after'] = data 241 242 if not self.module.check_mode: 243 user = self.api_query( 244 path="/v1/user/create", 245 method="POST", 246 data=data 247 ) 248 user.update(self.get_user()) 249 return user 250 251 def _update_user(self, user): 252 data = self._common_args() 253 data.update({ 254 'USERID': user['USERID'], 255 }) 256 257 force = self.module.params.get('force') 258 if not force: 259 del data['password'] 260 261 if force or self._has_changed(user=user, data=data): 262 self.result['changed'] = True 263 264 self.result['diff']['before'] = user 265 self.result['diff']['after'] = user.copy() 266 self.result['diff']['after'].update(data) 267 268 if not self.module.check_mode: 269 self.api_query( 270 path="/v1/user/update", 271 method="POST", 272 data=data 273 ) 274 user = self.get_user() 275 return user 276 277 def absent_user(self): 278 user = self.get_user() 279 if user: 280 self.result['changed'] = True 281 282 data = { 283 'USERID': user['USERID'], 284 } 285 286 self.result['diff']['before'] = user 287 self.result['diff']['after'] = {} 288 289 if not self.module.check_mode: 290 self.api_query( 291 path="/v1/user/delete", 292 method="POST", 293 data=data 294 ) 295 return user 296 297 298def main(): 299 argument_spec = vultr_argument_spec() 300 argument_spec.update(dict( 301 name=dict(type='str', required=True), 302 email=dict(type='str',), 303 password=dict(type='str', no_log=True), 304 force=dict(type='bool', default=False), 305 api_enabled=dict(type='bool', default=True), 306 acls=dict(type='list', choices=ACLS, aliases=['acl']), 307 state=dict(type='str', choices=['present', 'absent'], default='present'), 308 )) 309 310 module = AnsibleModule( 311 argument_spec=argument_spec, 312 required_if=[ 313 ('state', 'present', ['email', 'acls']), 314 ], 315 supports_check_mode=True, 316 ) 317 318 vultr_user = AnsibleVultrUser(module) 319 if module.params.get('state') == "absent": 320 user = vultr_user.absent_user() 321 else: 322 user = vultr_user.present_user() 323 324 result = vultr_user.get_result(user) 325 module.exit_json(**result) 326 327 328if __name__ == '__main__': 329 main() 330