1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# 4# Copyright: (c) 2018, Derek Rushing <derek.rushing@geekops.com> 5# Copyright: (c) 2018, VMware, Inc. 6# SPDX-License-Identifier: GPL-3.0-or-later 7# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 8 9from __future__ import absolute_import, division, print_function 10 11__metaclass__ = type 12 13 14DOCUMENTATION = r''' 15--- 16module: vmware_object_role_permission 17short_description: Manage local roles on an ESXi host 18description: This module can be used to manage object permissions on the given host. 19author: 20- Derek Rushing (@kryptsi) 21- Joseph Andreatta (@vmwjoseph) 22notes: 23 - Tested on ESXi 6.5, vSphere 6.7 24 - The ESXi login user must have the appropriate rights to administer permissions. 25 - Permissions for a distributed switch must be defined and managed on either the datacenter or a folder containing the switch. 26requirements: 27 - "python >= 2.7" 28 - PyVmomi 29options: 30 role: 31 description: 32 - The role to be assigned permission. 33 - User can also specify role name presented in Web UI. Supported added in 1.5.0. 34 required: True 35 type: str 36 principal: 37 description: 38 - The user to be assigned permission. 39 - Required if C(group) is not specified. 40 - If specifying domain user, required separator of domain uses backslash. 41 type: str 42 group: 43 description: 44 - The group to be assigned permission. 45 - Required if C(principal) is not specified. 46 type: str 47 object_name: 48 description: 49 - The object name to assigned permission. 50 type: str 51 required: True 52 object_type: 53 description: 54 - The object type being targeted. 55 default: 'Folder' 56 choices: ['Folder', 'VirtualMachine', 'Datacenter', 'ResourcePool', 57 'Datastore', 'Network', 'HostSystem', 'ComputeResource', 58 'ClusterComputeResource', 'DistributedVirtualSwitch'] 59 type: str 60 recursive: 61 description: 62 - Should the permissions be recursively applied. 63 default: True 64 type: bool 65 state: 66 description: 67 - Indicate desired state of the object's permission. 68 - When C(state=present), the permission will be added if it doesn't already exist. 69 - When C(state=absent), the permission is removed if it exists. 70 choices: ['present', 'absent'] 71 default: present 72 type: str 73extends_documentation_fragment: 74- community.vmware.vmware.documentation 75 76''' 77 78EXAMPLES = r''' 79- name: Assign user to VM folder 80 community.vmware.vmware_object_role_permission: 81 hostname: '{{ esxi_hostname }}' 82 username: '{{ esxi_username }}' 83 password: '{{ esxi_password }}' 84 role: Admin 85 principal: user_bob 86 object_name: services 87 state: present 88 delegate_to: localhost 89 90- name: Remove user from VM folder 91 community.vmware.vmware_object_role_permission: 92 hostname: '{{ esxi_hostname }}' 93 username: '{{ esxi_username }}' 94 password: '{{ esxi_password }}' 95 role: Admin 96 principal: user_bob 97 object_name: services 98 state: absent 99 delegate_to: localhost 100 101- name: Assign finance group to VM folder 102 community.vmware.vmware_object_role_permission: 103 hostname: '{{ esxi_hostname }}' 104 username: '{{ esxi_username }}' 105 password: '{{ esxi_password }}' 106 role: Limited Users 107 group: finance 108 object_name: Accounts 109 state: present 110 delegate_to: localhost 111 112- name: Assign view_user Read Only permission at root folder 113 community.vmware.vmware_object_role_permission: 114 hostname: '{{ esxi_hostname }}' 115 username: '{{ esxi_username }}' 116 password: '{{ esxi_password }}' 117 role: ReadOnly 118 principal: view_user 119 object_name: rootFolder 120 state: present 121 delegate_to: localhost 122 123- name: Assign domain user to VM folder 124 community.vmware.vmware_object_role_permission: 125 hostname: "{{ vcenter_hostname }}" 126 username: "{{ vcenter_username }}" 127 password: "{{ vcenter_password }}" 128 validate_certs: false 129 role: Admin 130 principal: "vsphere.local\\domainuser" 131 object_name: services 132 state: present 133 delegate_to: localhost 134''' 135 136RETURN = r''' 137changed: 138 description: whether or not a change was made to the object's role 139 returned: always 140 type: bool 141''' 142 143try: 144 from pyVmomi import vim, vmodl 145except ImportError: 146 pass 147 148from ansible.module_utils.basic import AnsibleModule 149from ansible.module_utils._text import to_native 150from ansible_collections.community.vmware.plugins.module_utils.vmware import PyVmomi, vmware_argument_spec, find_obj 151 152 153class VMwareObjectRolePermission(PyVmomi): 154 def __init__(self, module): 155 super(VMwareObjectRolePermission, self).__init__(module) 156 self.module = module 157 self.params = module.params 158 self.is_group = False 159 self.role_list = {} 160 self.role = None 161 self.auth_manager = self.content.authorizationManager 162 self.populate_role_list() 163 164 if self.params.get('principal', None) is not None: 165 self.applied_to = self.params['principal'] 166 elif self.params.get('group', None) is not None: 167 self.applied_to = self.params['group'] 168 self.is_group = True 169 170 self.get_role() 171 self.get_object() 172 self.get_perms() 173 self.perm = self.setup_permission() 174 self.state = self.params['state'] 175 176 def populate_role_list(self): 177 user_friendly_role_names = { 178 'Admin': ['Administrator'], 179 'ReadOnly': ['Read-Only'], 180 'com.vmware.Content.Admin': [ 181 'Content library administrator (sample)', 182 'Content library administrator' 183 ], 184 'NoCryptoAdmin': ['No cryptography administrator'], 185 'NoAccess': ['No access'], 186 'VirtualMachinePowerUser': [ 187 'Virtual machine power user (sample)', 188 'Virtual machine power user' 189 ], 190 'VirtualMachineUser': [ 191 'Virtual machine user (sample)', 192 'Virtual machine user' 193 ], 194 'ResourcePoolAdministrator': [ 195 'Resource pool administrator (sample)', 196 'Resource pool administrator' 197 ], 198 'VMwareConsolidatedBackupUser': [ 199 'VMware Consolidated Backup user (sample)', 200 'VMware Consolidated Backup user' 201 ], 202 'DatastoreConsumer': [ 203 'Datastore consumer (sample)', 204 'Datastore consumer' 205 ], 206 'NetworkConsumer': [ 207 'Network administrator (sample)', 208 'Network administrator' 209 ], 210 'VirtualMachineConsoleUser': ['Virtual Machine console user'], 211 'InventoryService.Tagging.TaggingAdmin': ['Tagging Admin'], 212 } 213 for role in self.auth_manager.roleList: 214 self.role_list[role.name] = role 215 if user_friendly_role_names.get(role.name): 216 for role_name in user_friendly_role_names[role.name]: 217 self.role_list[role_name] = role 218 219 def get_perms(self): 220 self.current_perms = self.auth_manager.RetrieveEntityPermissions(self.current_obj, False) 221 222 def same_permission(self, perm_one, perm_two): 223 return perm_one.principal.lower() == perm_two.principal.lower() \ 224 and perm_one.roleId == perm_two.roleId 225 226 def get_state(self): 227 for perm in self.current_perms: 228 if self.same_permission(self.perm, perm): 229 return 'present' 230 return 'absent' 231 232 def process_state(self): 233 local_permission_states = { 234 'absent': { 235 'present': self.remove_permission, 236 'absent': self.state_exit_unchanged, 237 }, 238 'present': { 239 'present': self.state_exit_unchanged, 240 'absent': self.add_permission, 241 } 242 } 243 try: 244 local_permission_states[self.state][self.get_state()]() 245 except vmodl.RuntimeFault as runtime_fault: 246 self.module.fail_json(msg=to_native(runtime_fault.msg)) 247 except vmodl.MethodFault as method_fault: 248 self.module.fail_json(msg=to_native(method_fault.msg)) 249 except Exception as e: 250 self.module.fail_json(msg=to_native(e)) 251 252 def state_exit_unchanged(self): 253 self.module.exit_json(changed=False) 254 255 def setup_permission(self): 256 perm = vim.AuthorizationManager.Permission() 257 perm.entity = self.current_obj 258 perm.group = self.is_group 259 perm.principal = self.applied_to 260 perm.roleId = self.role.roleId 261 perm.propagate = self.params['recursive'] 262 return perm 263 264 def add_permission(self): 265 if not self.module.check_mode: 266 self.auth_manager.SetEntityPermissions(self.current_obj, [self.perm]) 267 self.module.exit_json(changed=True) 268 269 def remove_permission(self): 270 if not self.module.check_mode: 271 self.auth_manager.RemoveEntityPermission(self.current_obj, self.applied_to, self.is_group) 272 self.module.exit_json(changed=True) 273 274 def get_role(self): 275 self.role = self.role_list.get(self.params['role'], None) 276 if not self.role: 277 self.module.fail_json(msg="Specified role (%s) was not found" % self.params['role']) 278 279 def get_object(self): 280 # find_obj doesn't include rootFolder 281 if self.params['object_type'] == 'Folder' and self.params['object_name'] == 'rootFolder': 282 self.current_obj = self.content.rootFolder 283 return 284 try: 285 getattr(vim, self.params['object_type']) 286 except AttributeError: 287 self.module.fail_json(msg="Object type %s is not valid." % self.params['object_type']) 288 self.current_obj = find_obj(content=self.content, 289 vimtype=[getattr(vim, self.params['object_type'])], 290 name=self.params['object_name']) 291 292 if self.current_obj is None: 293 self.module.fail_json( 294 msg="Specified object %s of type %s was not found." 295 % (self.params['object_name'], self.params['object_type']) 296 ) 297 if self.params['object_type'] == 'DistributedVirtualSwitch': 298 msg = "You are applying permissions to a Distributed vSwitch. " \ 299 "This will probably fail, since Distributed vSwitches inherits permissions " \ 300 "from the datacenter or a folder level. " \ 301 "Define permissions on the datacenter or the folder containing the switch." 302 self.module.warn(msg) 303 304 305def main(): 306 argument_spec = vmware_argument_spec() 307 argument_spec.update( 308 dict( 309 role=dict(required=True, type='str'), 310 object_name=dict(required=True, type='str'), 311 object_type=dict( 312 type='str', 313 default='Folder', 314 choices=[ 315 'Folder', 316 'VirtualMachine', 317 'Datacenter', 318 'ResourcePool', 319 'Datastore', 320 'Network', 321 'HostSystem', 322 'ComputeResource', 323 'ClusterComputeResource', 324 'DistributedVirtualSwitch', 325 ], 326 ), 327 principal=dict(type='str'), 328 group=dict(type='str'), 329 recursive=dict(type='bool', default=True), 330 state=dict(default='present', choices=['present', 'absent'], type='str'), 331 ) 332 ) 333 334 module = AnsibleModule( 335 argument_spec=argument_spec, 336 supports_check_mode=True, 337 mutually_exclusive=[ 338 ['principal', 'group'] 339 ], 340 required_one_of=[ 341 ['principal', 'group'] 342 ], 343 ) 344 345 vmware_object_permission = VMwareObjectRolePermission(module) 346 vmware_object_permission.process_state() 347 348 349if __name__ == '__main__': 350 main() 351