diff --git a/lib/ansible/modules/network/avi/avi_user.py b/lib/ansible/modules/network/avi/avi_user.py
new file mode 100644
index 00000000000..8963f9acee0
--- /dev/null
+++ b/lib/ansible/modules/network/avi/avi_user.py
@@ -0,0 +1,193 @@
+#!/usr/bin/python
+"""
+# Created on Aug 2, 2018
+#
+# @author: Shrikant Chaudhari (shrikant.chaudhari@avinetworks.com) GitHub ID: gitshrikant
+#
+# module_check: supported
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'}
+
+DOCUMENTATION = '''
+---
+module: avi_user
+author: Shrikant Chaudhari (@gitshrikant)
+short_description: Avi User Module
+description:
+ - This module can be used for creation, updation and deletion of a user.
+version_added: 2.9
+requirements: [ avisdk ]
+options:
+ state:
+ description:
+ - The state that should be applied on the entity.
+ default: present
+ choices: ["absent", "present"]
+ type: str
+ name:
+ description:
+ - Full name of the user.
+ required: true
+ type: str
+ obj_username:
+ description:
+ - Name that the user will supply when signing into Avi Vantage, such as jdoe or jdoe@avinetworks.com.
+ required: true
+ type: str
+ obj_password:
+ description:
+ - You may either enter a case-sensitive password in this field for the new or existing user.
+ required: true
+ type: str
+ email:
+ description:
+ - Email address of the user. This field is used when a user loses their password and requests to have it reset. See Password Recovery.
+ type: str
+ access:
+ description:
+ - Access settings (write, read, or no access) for each type of resource within Vantage.
+ type: list
+ is_superuser:
+ description:
+ - If the user will need to have the same privileges as the admin account, set it to true.
+ type: bool
+ is_active:
+ description:
+ - Activates the current user account.
+ type: bool
+ avi_api_update_method:
+ description:
+ - Default method for object update is HTTP PUT.
+ - Setting to patch will override that behavior to use HTTP PATCH.
+ default: put
+ choices: ["post", "put", "patch"]
+ type: str
+ avi_api_patch_op:
+ description:
+ - Patch operation to use when using avi_api_update_method as patch.
+ choices: ["add", "replace", "delete"]
+ type: str
+ user_profile_ref:
+ description:
+ - Refer user profile.
+ - This can also be full URI same as it comes in response payload
+ type: str
+ default_tenant_ref:
+ description:
+ - Default tenant reference.
+ - This can also be full URI same as it comes in response payload
+ default: /api/tenant?name=admin
+ type: str
+
+
+extends_documentation_fragment:
+ - avi
+'''
+
+EXAMPLES = '''
+ - name: user creation
+ avi_user:
+ controller: ""
+ username: ""
+ password: ""
+ api_version: ""
+ name: "testuser"
+ obj_username: "testuser"
+ obj_password: "test123"
+ email: "test@abc.test"
+ access:
+ - role_ref: "/api/role?name=Tenant-Admin"
+ tenant_ref: "/api/tenant/admin#admin"
+ user_profile_ref: "/api/useraccountprofile?name=Default-User-Account-Profile"
+ is_active: true
+ is_superuser: true
+ default_tenant_ref: "/api/tenant?name=admin"
+
+ - name: user creation
+ avi_user:
+ controller: ""
+ username: ""
+ password: ""
+ api_version: ""
+ name: "testuser"
+ obj_username: "testuser2"
+ obj_password: "password"
+ email: "testuser2@abc.test"
+ access:
+ - role_ref: "https://192.0.2.10/api/role?name=Tenant-Admin"
+ tenant_ref: "https://192.0.2.10/api/tenant/admin#admin"
+ user_profile_ref: "https://192.0.2.10/api/useraccountprofile?name=Default-User-Account-Profile"
+ is_active: true
+ is_superuser: true
+ default_tenant_ref: "https://192.0.2.10/api/tenant?name=admin"
+'''
+
+RETURN = '''
+obj:
+ description: Avi REST resource
+ returned: success, changed
+ type: dict
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+try:
+ from ansible.module_utils.network.avi.avi import (
+ avi_common_argument_spec, ansible_return, HAS_AVI)
+ from ansible.module_utils.network.avi.ansible_utils import (
+ avi_ansible_api)
+except ImportError:
+ HAS_AVI = False
+
+
+def main():
+ argument_specs = dict(
+ state=dict(default='present',
+ choices=['absent', 'present']),
+ name=dict(type='str', required=True),
+ obj_username=dict(type='str', required=True),
+ obj_password=dict(type='str', required=True, no_log=True),
+ access=dict(type='list',),
+ email=dict(type='str',),
+ is_superuser=dict(type='bool',),
+ is_active=dict(type='bool',),
+ avi_api_update_method=dict(default='put',
+ choices=['post', 'put', 'patch']),
+ avi_api_patch_op=dict(choices=['add', 'replace', 'delete']),
+ user_profile_ref=dict(type='str',),
+ default_tenant_ref=dict(type='str', default='/api/tenant?name=admin'),
+ )
+ argument_specs.update(avi_common_argument_spec())
+ module = AnsibleModule(argument_spec=argument_specs, supports_check_mode=True)
+ if not HAS_AVI:
+ return module.fail_json(msg=(
+ 'Avi python API SDK (avisdk>=17.1) or requests is not installed. '
+ 'For more details visit https://github.com/avinetworks/sdk.'))
+ return avi_ansible_api(module, 'user',
+ set([]))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/units/modules/network/avi/fixtures/avi_user.json b/test/units/modules/network/avi/fixtures/avi_user.json
new file mode 100644
index 00000000000..85522c324a1
--- /dev/null
+++ b/test/units/modules/network/avi/fixtures/avi_user.json
@@ -0,0 +1,215 @@
+{
+ "mock_create_res": {
+ "ansible_facts": {
+ "avi_api_context": {
+ "192.0.2.97:admin:None": {
+ "csrftoken": "qG23CCARDL3rh1KZ66XXPIeUYCUCOZ4q",
+ "session_id": "h5nynf9u9nompp5byai7vii2v8bbn9kd"
+ }
+ }
+ },
+ "api_context": null,
+ "changed": true,
+ "invocation": {
+ "module_args": {
+ "access": [{
+ "role_ref": "/api/role?name=Tenant-Admin",
+ "tenant_ref": "/api/tenant/********#********",
+ "all_tenants": false
+ }],
+ "api_context": null,
+ "api_version": "18.2.5",
+ "avi_api_update_method": "put",
+ "avi_credentials": null,
+ "avi_disable_session_cache_as_fact": false,
+ "avi_login_info": null,
+ "controller": "192.0.2.97",
+ "default_tenant_ref": "/api/tenant?name=********",
+ "email": "test@abc.com",
+ "is_active": true,
+ "is_superuser": true,
+ "name": "testuser",
+ "obj_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "obj_username": "testuser",
+ "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "state": "present",
+ "tenant": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "tenant_uuid": "",
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
+ }
+ },
+ "obj": {
+ "_last_modified": "1559736767460818",
+ "access": [{
+ "all_tenants": false,
+ "role_ref": "https://192.0.2.97/api/tenant/********/role/role-ff851004-bd75-485b-87ec-2fe1d6a03fb9#Tenant-Admin",
+ "tenant_ref": "https://192.0.2.97/api/tenant/********#********"
+ }],
+ "default_tenant_ref": "https://192.0.2.97/api/tenant/********#********",
+ "email": "test@abc.com",
+ "full_name": "testuser",
+ "is_active": true,
+ "is_superuser": true,
+ "local": true,
+ "name": "testuser",
+ "obj_password": "",
+ "obj_username": "testuser",
+ "password": "",
+ "uid": 2004,
+ "url": "https://192.0.2.97/api/user/user-7087578f-4dfe-4e06-a153-495a91824a1d#testuser",
+ "user_profile_ref": "https://192.0.2.97/api/useraccountprofile/useraccountprofile-78063e7c-b443-48d6-b34c-5253ae1fcd2a#Default-User-Account-Profile",
+ "username": "testuser",
+ "uuid": "user-7087578f-4dfe-4e06-a153-495a91824a1d"
+ },
+ "old_obj": null
+ },
+ "mock_put_res": {
+ "obj": {
+ "username": "testuser",
+ "user_profile_ref": "https://192.0.2.97/api/useraccountprofile/useraccountprofile-546c5e88-6270-4ba1-9cfd-d0c755e68f47#Default-User-Account-Profile",
+ "name": "testuser",
+ "url": "https://192.0.2.97/api/user/user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a#testuser",
+ "is_active": true,
+ "uuid": "user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a",
+ "email": "newemail@abc.com",
+ "access": [{
+ "tenant_ref": "https://192.0.2.97/api/tenant/tenant-57af0f3f-6f14-4657-8f32-9b289407752b#Test-Admin",
+ "all_tenants": false,
+ "role_ref": "https://192.0.2.97/api/tenant/********/role/role-b073ab0d-e1d0-4800-95ef-6ecf2c5ed7d1#Tenant-Admin"
+ }],
+ "is_superuser": true,
+ "obj_username": "testuser",
+ "full_name": "testuser",
+ "_last_modified": "1559802772203285",
+ "password": "",
+ "local": true,
+ "obj_password": "",
+ "default_tenant_ref": "https://192.0.2.97/api/tenant/********#********",
+ "uid": 2002
+ },
+ "changed": true,
+ "api_context": null,
+ "invocation": {
+ "module_args": {
+ "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "api_version": "18.2.5",
+ "name": "testuser",
+ "state": "present",
+ "is_active": true,
+ "api_context": null,
+ "avi_disable_session_cache_as_fact": false,
+ "controller": "192.0.2.97",
+ "avi_api_patch_op": null,
+ "access": [{
+ "tenant_ref": "/api/tenant?name=Test-Admin",
+ "all_tenants": false,
+ "role_ref": "/api/role?name=Tenant-Admin"
+ }],
+ "is_superuser": true,
+ "avi_credentials": null,
+ "email": "newemail@abc.com",
+ "default_tenant_ref": "/api/tenant?name=********",
+ "obj_username": "testuser",
+ "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "tenant_uuid": "",
+ "obj_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "avi_api_update_method": "put",
+ "tenant": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
+ }
+ },
+ "ansible_facts": {
+ "avi_api_context": {
+ "192.0.2.97:admin:None": {
+ "csrftoken": "Y7CET6zaIC9VZAzBqEW4cWo1N26jPg55",
+ "session_id": "364n7o0p3o5so63b9rzd47v6ehya6xg7"
+ }
+ }
+ },
+ "old_obj": {
+ "username": "testuser",
+ "user_profile_ref": "https://192.0.2.97/api/useraccountprofile/useraccountprofile-546c5e88-6270-4ba1-9cfd-d0c755e68f47#Default-User-Account-Profile",
+ "name": "testuser",
+ "url": "https://192.0.2.97/api/user/user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a#testuser",
+ "is_active": true,
+ "uuid": "user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a",
+ "access": [{
+ "tenant_ref": "https://192.0.2.97/api/tenant/tenant-57af0f3f-6f14-4657-8f32-9b289407752b#Test-Admin",
+ "all_tenants": false,
+ "role_ref": "https://192.0.2.97/api/tenant/********/role/role-b073ab0d-e1d0-4800-95ef-6ecf2c5ed7d1#Tenant-Admin"
+ }],
+ "is_superuser": true,
+ "full_name": "testuser",
+ "ui_property": "",
+ "password": "",
+ "local": true,
+ "email": "test@abc.com",
+ "default_tenant_ref": "https://192.0.2.97/api/tenant/********#********",
+ "uid": 2002
+ }
+ },
+ "mock_del_res": {
+ "ansible_facts": {
+ "avi_api_context": {
+ "192.0.2.97:admin:None": {
+ "csrftoken": "Vtkx9GeS2lsrld5yX83cmJqbZO3MAimb",
+ "session_id": "ix3t1dja8yzwb155de59viyn96hibn6b"
+ }
+ }
+ },
+ "api_context": null,
+ "changed": true,
+ "invocation": {
+ "module_args": {
+ "access": [{
+ "role_ref": "/api/role?name=Tenant-Admin",
+ "tenant_ref": "/api/tenant/********#********"
+ }],
+ "api_context": null,
+ "api_version": "18.2.5",
+ "avi_api_update_method": "put",
+ "avi_credentials": null,
+ "avi_disable_session_cache_as_fact": false,
+ "avi_login_info": null,
+ "controller": "192.0.2.97",
+ "default_tenant_ref": "/api/tenant?name=********",
+ "email": "test@abc.com",
+ "is_active": true,
+ "is_superuser": true,
+ "name": "testuser",
+ "obj_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "obj_username": "testuser",
+ "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "state": "absent",
+ "tenant": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
+ "tenant_uuid": "",
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
+ }
+ },
+ "obj": null,
+ "old_obj": {
+ "_last_modified": "1559803346264869",
+ "access": [{
+ "all_tenants": false,
+ "role_ref": "https://192.0.2.97/api/tenant/********/role/role-b073ab0d-e1d0-4800-95ef-6ecf2c5ed7d1#Tenant-Admin",
+ "tenant_ref": "https://192.0.2.97/api/tenant/tenant-57af0f3f-6f14-4657-8f32-9b289407752b#Test-Admin"
+ }],
+ "default_tenant_ref": "https://192.0.2.97/api/tenant/********#********",
+ "email": "newemail@abc.com",
+ "full_name": "testuser",
+ "is_active": true,
+ "is_superuser": true,
+ "local": true,
+ "name": "testuser",
+ "password": "",
+ "ui_property": "",
+ "uid": 2002,
+ "url": "https://192.0.2.97/api/user/user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a#testuser",
+ "user_profile_ref": "https://192.0.2.97/api/useraccountprofile/useraccountprofile-546c5e88-6270-4ba1-9cfd-d0c755e68f47#Default-User-Account-Profile",
+ "username": "testuser",
+ "uuid": "user-ed10f328-bd92-4db2-bacd-0cf795fcbf8a"
+ }
+ }
+}
diff --git a/test/units/modules/network/avi/test_avi_user.py b/test/units/modules/network/avi/test_avi_user.py
new file mode 100644
index 00000000000..c4efb87ceac
--- /dev/null
+++ b/test/units/modules/network/avi/test_avi_user.py
@@ -0,0 +1,101 @@
+import os
+import json
+from units.compat import unittest
+from units.compat.mock import Mock
+from units.modules.utils import set_module_args
+from ansible.modules.network.avi import avi_user
+
+fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
+with open(fixture_path + '/avi_user.json') as json_file:
+ data = json.load(json_file)
+
+
+class TestAviUser(unittest.TestCase):
+
+ def test_create_user(self):
+ set_module_args({
+ "avi_credentials": {
+ "controller": "192.0.2.13",
+ "username": "username",
+ "password": "fakepassword",
+ "api_version": "18.2.5"
+ },
+ "state": "present",
+ "name": "testuser",
+ "obj_username": "testuser",
+ "obj_password": "test123",
+ "email": "test@abc.com",
+ "access": [
+ {
+ "role_ref": "/api/role?name=Tenant-Admin",
+ "tenant_ref": "/api/tenant?name=Test-Admin",
+ "all_tenants": False
+ }
+ ],
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "is_active": True,
+ "is_superuser": True,
+ "default_tenant_ref": "/api/tenant?name=admin"
+ })
+ avi_user.avi_ansible_api = Mock(return_value=data['mock_create_res'])
+ response = avi_user.main()
+ assert response['changed']
+
+ def test_put_on_user(self):
+ set_module_args({
+ "avi_credentials": {
+ "controller": "192.0.2.13",
+ "username": "username",
+ "password": "fakepassword",
+ "api_version": "18.2.5"
+ },
+ "state": "present",
+ "avi_api_update_method": "put",
+ "name": "testuser",
+ "obj_username": "testuser",
+ "obj_password": "test123",
+ "email": "newemail@abc.com",
+ "access": [{
+ "role_ref": "/api/role?name=Tenant-Admin",
+ "tenant_ref": "/api/tenant?name=Test-Admin",
+ "all_tenants": False
+ }],
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "is_active": True,
+ "is_superuser": True,
+ "default_tenant_ref": "/api/tenant?name=admin"
+ })
+ avi_user.avi_ansible_api = Mock(return_value=data['mock_put_res'])
+ response = avi_user.main()
+ assert response['changed']
+ assert response['obj']
+ assert response['old_obj']
+
+ def test_delete_user(self):
+ set_module_args({
+ "avi_credentials": {
+ "controller": "192.0.2.13",
+ "username": "username",
+ "password": "fakepassword",
+ "api_version": "18.2.5"
+
+ },
+ "name": "testuser",
+ "obj_username": "testuser",
+ "obj_password": "test123",
+ "email": "test@abc.com",
+ "access": [{
+ "role_ref": "/api/role?name=Tenant-Admin",
+ "tenant_ref": "/api/tenant?name=Test-Admin",
+ "all_tenants": False
+ }],
+ "user_profile_ref": "/api/useraccountprofile?name=Default-User-Account-Profile",
+ "is_active": True,
+ "is_superuser": True,
+ "default_tenant_ref": "/api/tenant?name=admin"
+ })
+ avi_user.avi_ansible_api = Mock(return_value=data['mock_del_res'])
+ response = avi_user.main()
+ assert response['changed']
+ assert not response['obj']
+ assert response['old_obj']