ansible-infra/ansible_collections/grafana/grafana/plugins/modules/user.py
Stefan Bethke 2aed20393f
Some checks failed
/ Ansible Lint (push) Failing after 5m45s
/ Ansible Lint (pull_request) Failing after 4m59s
Vendor Galaxy Roles and Collections
2026-02-06 22:07:16 +01:00

284 lines
8.5 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, téïcée (www.teicee.com)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
DOCUMENTATION = '''
---
module: user
author:
- Mathieu Valois, téïcée
version_added: "0.0.1"
short_description: Manage Users in Grafana
description:
- Create, Update and delete Users using Ansible.
requirements: [ "requests >= 1.0.0" ]
notes:
- Does not support C(check_mode).
- Does not support C(Idempotency).
options:
grafana_url:
description:
- URL of the Grafana instance.
type: str
required: true
admin_name:
description:
- Grafana admin username
type: str
required : true
admin_password:
description:
- Grafana admin password
type: str
required : true
login:
description:
- Login of the user
type: str
required : true
password:
description:
- Password of the user. Should be provided if state=present
type: str
required : false
name:
description:
- Name of the user.
type: str
required : false
email:
description:
- Email address of the user.
type: str
required : false
state:
description:
- State for the Grafana User.
choices: [ present, absent ]
default: present
type: str
'''
EXAMPLES = '''
- name: Create/Update a user
grafana.grafana.user:
login: "grafana_user"
password: "{{ lookup('ansible.builtin.password') }}"
email: "grafana_user@localhost.local
name: "grafana user"
grafana_url: "{{ grafana_url }}"
admin_name: "admin"
admin_password: "admin"
state: present
- name: Delete user
grafana.grafana.user:
login: "grafana_user"
grafana_url: "{{ grafana_url }}"
admin_name: "admin"
admin_password: "admin"
state: absent
'''
RETURN = r'''
output:
description: Dict object containing user information and message.
returned: On success
type: dict
contains:
id:
description: The ID for the user.
returned: on success
type: int
sample: 17
email:
description: The email for the user.
returned: on success
type: str
sample: grafana_user@localhost.local
name:
description: The name for the user.
returned: on success
type: str
sample: grafana user
login:
description: The login for the user.
returned: on success
type: str
sample: grafana_user
'''
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
__metaclass__ = type
def _get_user(grafana_url, admin_name, admin_password, login, email=None):
get_user_url = grafana_url + '/api/users/lookup?loginOrEmail='
# check if user exists by login provided login
result = requests.get(f"{get_user_url}{login}", auth=requests.auth.HTTPBasicAuth(
admin_name, admin_password))
# if no user has this login, check the email if provided
if result.status_code == 404 and email is not None:
result = requests.get(f"{get_user_url}{email}", auth=requests.auth.HTTPBasicAuth(
admin_name, admin_password))
if result.status_code == 404:
return None
return result.json()
def _set_user_password(grafana_url, admin_name, admin_password, user_id, password):
""" sets the password for the existing user having user_id.
admin_name should be a user having users.password:write permission
"""
set_user_password_url = f"{grafana_url}/api/admin/users/{user_id}/password"
result = requests.put(set_user_password_url, json={'password': password}, auth=requests.auth.HTTPBasicAuth(
admin_name, admin_password))
return result
def present_user(module):
if module.params['grafana_url'][-1] == '/':
module.params['grafana_url'] = module.params['grafana_url'][:-1]
body = {
'login': module.params['login'],
'password': module.params['password'],
'email': module.params['email'],
'name': module.params['name'],
'OrgId': module.params['orgid']
}
user = _get_user(module.params['grafana_url'], module.params['admin_name'],
module.params['admin_password'], module.params['login'], module.params['email'])
if user is None:
api_url = module.params['grafana_url'] + '/api/admin/users'
result = requests.post(api_url, json=body, auth=requests.auth.HTTPBasicAuth(
module.params['admin_name'], module.params['admin_password']))
else:
user_id = user['id']
api_url = module.params['grafana_url'] + '/api/users'
result = requests.put(f"{api_url}/{user_id}", json=body, auth=requests.auth.HTTPBasicAuth(
module.params['admin_name'], module.params['admin_password']))
if result.status_code == 200:
return False, True, result.json()
return True, False, {"status": result.status_code, 'response': result.json()['message']}
def absent_user(module):
if module.params['grafana_url'][-1] == '/':
module.params['grafana_url'] = module.params['grafana_url'][:-1]
user = _get_user(module.params['grafana_url'], module.params['admin_name'],
module.params['admin_password'], module.params['login'], module.params['email'])
if user is None:
return False, False, "User does not exist"
user_id = user['id']
api_url = f"{module.params['grafana_url']}/api/admin/users/{user_id}"
result = requests.delete(api_url, auth=requests.auth.HTTPBasicAuth(
module.params['admin_name'], module.params['admin_password']))
if result.status_code == 200:
return False, True, result.json()
return True, False, {"status": result.status_code, 'response': result.json()['message']}
def password_user(module):
if module.params['grafana_url'][-1] == '/':
module.params['grafana_url'] = module.params['grafana_url'][:-1]
# try with new password to check if already changed
user = _get_user(module.params['grafana_url'], module.params['login'],
module.params['password'], module.params['login'], module.params['email'])
if 'id' in user:
# Auth is OK, password does not need to be changed
return False, False, {'message': 'Password has already been changed', 'user': user}
# from here, we begin password change procedure
user = _get_user(module.params['grafana_url'], module.params['admin_name'],
module.params['admin_password'], module.params['login'], module.params['email'])
if user is None:
return True, False, "User does not exist"
if 'id' not in user:
return True, False, user
result = _set_user_password(module.params['grafana_url'], module.params['admin_name'],
module.params['admin_password'], user['id'], module.params['password'])
if result.status_code == 200:
return False, True, result.json()
return True, False, result.json()
def main():
# Grafana admin API is only accessible with basic auth, not token
# So we shall provide admin name and its password
module_args = dict(
admin_name=dict(type='str', required=True),
admin_password=dict(type='str', required=True, no_log=True),
login=dict(type='str', required=True),
password=dict(type='str', required=False, no_log=True),
email=dict(type='str', required=False),
name=dict(type='str', required=False),
orgid=dict(type='int', required=False),
grafana_url=dict(type='str', required=True),
state=dict(type='str', required=False, default='present',
choices=['present', 'absent', 'update_password'])
)
choice_map = {
"present": present_user,
"absent": absent_user,
"update_password": password_user
}
module = AnsibleModule(
argument_spec=module_args
)
if not HAS_REQUESTS:
module.fail_json(msg=missing_required_lib('requests'))
if module.params['state'] in ('present', 'update_password') and 'password' not in module.params:
module.fail_json(
msg="Want to create or update user but password is missing")
is_error, has_changed, result = choice_map.get(
module.params['state'])(module)
if not is_error:
module.exit_json(changed=has_changed, output=result)
else:
module.fail_json(msg=result)
if __name__ == '__main__':
main()