You can use a custom Python script to create a restricted vCenter user to register the DRaaS Connector with vCenter.

This script allows you to create a vCenter user with restricted roles that contain the minimim set of privileges required by the DRaaS Connector to perform snapshot replication, failover, and failback operations. Run this script from the DRaaS Connector VM command line.

Prerequisites

  • GOVC installed on the system where you run this script. You can download GOVC here: https://github.com/vmware/govmomi/tree/master/govc#readme.
  • Set the GOVC_PATH variable in the script to the path to the 'govc' executable on the system where you run this script. The default is /usr/local/bin/govc.
  • Administrator user account and password from the vCenter where you are creating this user. You must pass these credentials when you run the script.

Usage

Create a vCenter user and role with a minimum set of privileges needed by the DRaaS Connector. Optional commands appear inside square brackets [ ].

vcdr-create-vcenter-user.py [-h]

Options:

Option Description

-h, --help

Displays a list of commands and options.

--vcenter VCENTER

IP address of the vCenter where you create this user.

--admin-username admin-username

vCenter Administrator user name.

You must create this user in vCenter before you run the CLI commands. If not supplied, the script prompts you for this user name.

-

[-admin-password admin-password]

vCenter Administrator password. If not supplied, the script prompts you for this password.

--new-username new-username

User name for the new user.

[--new-password new-password]

Password for the new user.

--vcenter-role vcenter-role

Name of new or existing vCenter role to associate to the new user. You can apply --snapshot-privs and/or --failback-privs to this role.

[--keep-existing-privs]

Keep existing privileges on the provided role. Using this option ensures that you do not unecessarily remove privileges from the provided role.

[--create-role-only]

Create role with necessary privileges, but do not create a user account or assign permissions.

[--snapshot-privs]

This option adds snapshot and failover privileges to the user role.

[--failback-privs]

This option adds snapshot, failover, and failback privileges to the user role.

Example

The following example shows how to use the CLI to create a vCenter role called vcdr-dkup-role and assign the role snapshot and failover privileges (but no failback). This command then creates a vCenter user named vcdr-bkup-user@vmware.local, and globally assigns this new user the new role.

connector-name>> vcdr-create-vcenter-user.py --vcenter 192.0.170.23 --admin-username administrator@vmware.local --new-username vcdr-bkup-user@vmware.local --vcenter-role vcdr-bkup-role --snapshot-privs

Script

x
1 #!/usr/bin/env python
2 
3 """
4 Copyright 2021 VMware, Inc.  All rights reserved.
5 
6 ==========================
7 IMPORT! READ THE FOLLOWING
8 ==========================
9 
10 This script can be  downloaded and used by customers to create a vCenter user,
11 role, and permission for use with vCenter registration with the VMware Cloud
12 Disaster Recover (VCDR) product.
13 
14 REQUIREMENTS
15 
16  - Set the GOVC_PATH variable, below, to the path to the 'govc' executable on
17    the system where this script will run. The default is '/usr/local/bin/govc'.
18 
19  - GOVC
20    GOVC must be installed in order for this script to work.
21    https://github.com/vmware/govmomi/tree/master/govc#readme
22 
23 EXAMPLE
24 
25 $ vcdr-create-vcenter-user.py --vcenter 10.80.15.23 --admin-username administrator@vmware.local \
26   --new-username vcdr-bkup-user@vmware.local --vcenter-role vcdr-bkup-role --snapshot-privs
27 
28 The above will create a vCenter role called vcdr-dkup-role with the privileges necessary to perform
29 VM backup (i.e., it will not work for failback). It will also create a vCenter user called
30 vcdr-bkup-user@vmware.local.  Finally, it will globally assign this new user the above role.
31 
32 USAGE
33 
34 $ vcdr-create-vcenter-user.py --help
35 
36 usage: vcdr-create-vcenter-user.py [-h] --vcenter VCENTER --admin-username
37                                   admin-username --new-username new-username
38                                   [--admin-password admin-password]
39                                   [--new-password new-password] --vcenter-role
40                                   vcenter-role [--keep-existing-privs]
41                                   [--create-role-only]
42                                   [--snapshot-privs | --failback-privs]
43 
44 Create vCenter user/role/permission with minimal privileges for use with VCDR.
45 
46 optional arguments:
47   -h, --help            show this help message and exit
48   --vcenter VCENTER     vCenter IP on which to create/update user
49   --admin-username admin-username
50                         Admin username at desired vCenter
51   --new-username new-username
52                         New username to be created in the desired vCenter
53   --admin-password admin-password
54                         Admin password at desired vCenter (will be prompted if
55                         not provided)
56   --new-password new-password
57                         New password for the new user in the desired vCenter
58                         (will be prompted if not provided)
59   --vcenter-role vcenter-role
60                         Name of new or existing vCenter role to associate to
61                         the new user
62   --keep-existing-privs
63                         Keep existing privileges on provided role (i.e., do
64                         not prune unnecessary privileges from provided role)
65   --create-role-only    Create role with necessary privileges but do not
66                         create a user account or assign permissions
67   --snapshot-privs      Create a user with privileges necessary to snapshot
68                         VMs and failover (the default)
69   --failback-privs      Create a user with privileges necessary to snapshot
70                         VMs, failover, and failback
71 
72 MISC
73 
74 This script cannot be used to create/update localos users (i.e., the created
75 VCDR user account must be of the form 'name@domain', e.g., 'Administrator@vmware.local').
76 
77 This script has been tested with python3.
78 """
79 __author__ = "VMware, Inc"
80 
81 from getpass import getpass
82 import json
83 import logging
84 import os
85 import subprocess
86 import time
87 
88 logger = logging.getLogger(__name__)
89 logger.setLevel(logging.DEBUG)
90 
91 GOVC_PATH = '/usr/local/bin/govc'
92 LOG_FILE = '/var/tmp/vcdr-create-vcenter-user'
93 
94 VCDR_SNAPSHOT_PRIVS = [
95     'Datastore.Browse',
96     'Datastore.FileManagement',
97     'Global.DisableMethods',
98     'Global.EnableMethods',
99     'System.Anonymous',
100     'System.Read',
101     'System.View',
102     'VirtualMachine.Config.ChangeTracking',
103     'VirtualMachine.Config.DiskLease',
104     'VirtualMachine.Config.QueryUnownedFiles',
105     'VirtualMachine.Config.ReloadFromPath',
106     'VirtualMachine.Provisioning.DiskRandomRead',
107     'VirtualMachine.Provisioning.GetVmFiles',
108     'VirtualMachine.Provisioning.ReadCustSpecs',
109     'VirtualMachine.State.CreateSnapshot',
110     'VirtualMachine.State.RemoveSnapshot'
111 ]
112 
113 VCDR_FAILBACK_PRIVS = VCDR_SNAPSHOT_PRIVS + [
114     'Datastore.AllocateSpace',
115     'Datastore.Config',
116     'Datastore.DeleteFile',
117     'Datastore.UpdateVirtualMachineFiles',
118     'Datastore.UpdateVirtualMachineMetadata',
119     'Global.CancelTask',
120     'Global.GlobalTag',
121     'Global.Licenses',
122     'Global.ManageCustomFields',
123     'Global.SetCustomField',
124     'InventoryService.Tagging.AttachTag',
125     'Network.Assign',
126     'Resource.AssignVAppToPool',
127     'Resource.AssignVMToPool',
128     'Sessions.GlobalMessage',
129     'Sessions.ValidateSession',
130     'VirtualMachine.Config.AddExistingDisk',
131     'VirtualMachine.Config.AddNewDisk',
132     'VirtualMachine.Config.AddRemoveDevice',
133     'VirtualMachine.Config.AdvancedConfig',
134     'VirtualMachine.Config.CPUCount',
135     'VirtualMachine.Config.DiskExtend',
136     'VirtualMachine.Config.EditDevice',
137     'VirtualMachine.Config.HostUSBDevice',
138     'VirtualMachine.Config.ManagedBy',
139     'VirtualMachine.Config.Memory',
140     'VirtualMachine.Config.MksControl',
141     'VirtualMachine.Config.QueryFTCompatibility',
142     'VirtualMachine.Config.RawDevice',
143     'VirtualMachine.Config.RemoveDisk',
144     'VirtualMachine.Config.Rename',
145     'VirtualMachine.Config.ResetGuestInfo',
146     'VirtualMachine.Config.Resource',
147     'VirtualMachine.Config.Settings',
148     'VirtualMachine.Config.SwapPlacement',
149     'VirtualMachine.GuestOperations.Execute',
150     'VirtualMachine.GuestOperations.Modify',
151     'VirtualMachine.GuestOperations.Query',
152     'VirtualMachine.Interact.Backup',
153     'VirtualMachine.Interact.DeviceConnection',
154     'VirtualMachine.Interact.GuestControl',
155     'VirtualMachine.Interact.PowerOff',
156     'VirtualMachine.Interact.PowerOn',
157     'VirtualMachine.Interact.SetCDMedia',
158     'VirtualMachine.Interact.SetFloppyMedia',
159     'VirtualMachine.Interact.ToolsInstall',
160     'VirtualMachine.Inventory.Create',
161     'VirtualMachine.Inventory.CreateFromExisting',
162     'VirtualMachine.Inventory.Move',
163     'VirtualMachine.Inventory.Register',
164     'VirtualMachine.Inventory.Unregister',
165     'VirtualMachine.Provisioning.Customize',
166     'VirtualMachine.Provisioning.DiskRandomAccess',
167     'VirtualMachine.Provisioning.FileRandomAccess',
168     'VirtualMachine.Provisioning.MarkAsVM',
169     'VirtualMachine.Provisioning.ModifyCustSpecs',
170     'VirtualMachine.Provisioning.PromoteDisks',
171     'VirtualMachine.State.RevertToSnapshot'
172 ]
173 
174 VCDR_LWD_PRIVS = [
175     'vSphereDataProtection.Protection',
176     'Host.Config.NetService',
177     'vSphereDataProtection.Recovery',
178     'System.Read',
179     'System.Anonymous',
180     'Host.Config.Storage',
181 ]
182 
183 class GOVC(object):
184     def __init__(self, vc_url, vc_username, vc_password, verify_server_cert=False, exit_on_cmd_failure=True):
185         """
186         The govc URL scheme defaults to https and the URL path defaults to /sdk.
187         This means that:
188         1. 'host' is equivalent to 'https://<host>/sdk'.
189         2. 'username:password@host' is equivalent to 'https://<username:password@host>/sdk'.    # pragma: allowlist secret
190 
191         :param vc_url: URL of ESXi or vCenter instance to connect to
192         :param vc_username: vCenter or ESXi admin user's username
193         :param vc_password: vCenter or ESXi admin user's password
194         :param verify_server_cert: Verify vCenter or ESXi server certificate
195         :return: None
196         """
197         self.vc_url = vc_url
198         self.vc_username = vc_username
199         self.vc_password = vc_password
200         self.verify_server_cert = verify_server_cert
201         self.exit_on_cmd_failure = exit_on_cmd_failure
202         self.set_envs()
203 
204     def _run_govc_cli(self, cmd_args, timeout, json=False):
205         """
206         Run command.
207 
208         :param cmd_args: List of command line args
209         :param json: Enable JSON output
210         :param timeout: command timeout in secs
211         :return: command output
212         """
213         cmd = [GOVC_PATH]
214         cmd.extend(cmd_args)
215         if json:
216             cmd.insert(2, '-json')
217 
218         wait = 0
219         logger.debug('Run command %s.', cmd)
220         p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
221         while p.poll() is None and wait < timeout:
222             wait += 1
223             time.sleep(1)
224 
225         if p.poll() is None:
226             p.kill()
227             rc = 124
228         else:
229             rc = p.returncode
230         stdout, stderr = p.communicate()
231 
232         if rc != 0:
233             if rc == 124:
234                 stderr = 'timed out after {} secs, {}'.format(timeout, stderr)
235             logger.error('%s failed, rc: %d, stdout: %s, stderr: %s', cmd, rc, stdout, stderr)
236             if self.exit_on_cmd_failure:
237                 exit(rc)
238             else:
239                 raise subprocess.CalledProcessError(rc, cmd, output=stdout+stderr)
240 
241         logger.info(stdout)
242         return stdout.strip()
243 
244     def set_envs(self):
245         """
246         Set govc login environment variables.
247 
248         While using '-u https://user:pass@host/sdk' from the govc command line,    # pragma: allowlist secret
249         there is a bug if the username or the password includes special characters.
250         Using environment variables instead confirmed works.
251         """
252         os.environ['GOVC_URL'] = self.vc_url
253         os.environ['GOVC_USERNAME'] = self.vc_username
254         os.environ['GOVC_PASSWORD'] = self.vc_password
255         os.environ['GOVC_INSECURE'] = 'false' if self.verify_server_cert else 'true'
256         logger.debug('govc envs: %s', {k: v for k, v in os.environ.items() if k.startswith('GOVC') and k != 'GOVC_PASSWORD'})
257 
258     def ssogroup_create(self, name, description=None, timeout=15, json=False, **kwargs):
259         """
260         Create SSO group.
261 
262         :param name: SSO group name
263         :param description: SSO group description
264         :param timeout: command timeout in secs
265         :param json: Enable JSON output
266         :return: Command output
267         """
268         kwargs.clear()
269 
270         cmd_args = ['sso.group.create']
271         if description:
272             cmd_args.extend(['-d', description])
273         cmd_args.append(name)
274 
275         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
276 
277     def ssogroup_ls(self, timeout=15, json=False, **kwargs):
278         """
279         List SSO groups.
280 
281         :param timeout: command timeout in secs
282         :param json: Enable JSON output
283         :return: SSO groups
284         """
285         kwargs.clear()
286 
287         cmd_args = ['sso.group.ls']
288 
289         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
290 
291     def ssogroup_rm(self, name, timeout=15, json=False, **kwargs):
292         """
293         Remove SSO group.
294 
295         :param name: SSO group name
296         :param timeout: command timeout in secs
297         :param json: Enable JSON output
298         :return: Command output
299         """
300         kwargs.clear()
301 
302         cmd_args = ['sso.group.rm', name]
303 
304         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
305 
306     def ssogroup_update(self, name, description='', add_user=None, remove_user=None,
307                         timeout=15, json=False, **kwargs):
308         """
309         Update SSO group.
310 
311         :param name: SSO group name
312         :param description: SSO group description
313         :param add_user: Add user to group
314         :param remove_user: Remove user from group
315         :param description: SSO group description
316         :param timeout: command timeout in secs
317         :param json: Enable JSON output
318         :return: Command output
319         """
320         kwargs.clear()
321 
322         cmd_args = ['sso.group.update']
323         if description:
324             cmd_args.extend(['-d', description])
325         if add_user is not None:
326             cmd_args.extend(['-a', add_user])
327         if remove_user is not None:
328             cmd_args.extend(['-r', remove_user])
329         cmd_args.append(name)
330 
331         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
332 
333     def ssouser_create(self, name, password=None, description=None,
334                        solution=False, act_as_user=True, role='Administrator', certificate=None,
335                        timeout=15, json=False, **kwargs):
336         """
337         Create SSO user.
338 
339         :param name: SSO user name
340         :param password: SSO person user password
341         :param description: SSO user description
342         :param solution: Whether is solution user
343         :param act_as_user: ActAsUser role for solution user WSTrust
344         :param role: Role for solution user (RegularUser|Administrator)
345         :param certificate: Certificate for solution user
346         :param timeout: command timeout in secs
347         :param json: Enable JSON output
348         :return: Command output
349         """
350         kwargs.clear()
351 
352         cmd_args = ['sso.user.create']
353         if description:
354             cmd_args.extend(['-d', description])
355         if solution:
356             assert certificate, 'Solution user certificate is required!'
357             assert role in ['RegularUser', 'Administrator'], (
358                 'Invalid solution user role: {}!'.format(role))
359             if act_as_user:
360                 cmd_args.append('-A')
361             cmd_args.extend(['-R', role, '-C', certificate])
362         else:
363             # Person user.
364             assert password, 'SSO user password is required!'
365             cmd_args.extend(['-p', password])
366         cmd_args.append(name)
367 
368         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
369 
370     def ssouser_id(self, name=None, timeout=15, json=False, **kwargs):
371         """
372         Get SSO user and group IDs.
373 
374         :param name: SSO user name
375         :param timeout: command timeout in secs
376         :param json: Enable JSON output
377         :return: SSO user and group IDs
378         """
379         kwargs.clear()
380 
381         cmd_args = ['sso.user.id']
382         if name is not None:
383             cmd_args.append(name)
384 
385         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
386 
387     def ssouser_ls(self, solution=False, timeout=15, json=False, **kwargs):
388         """
389         List SSO users.
390 
391         :param solution: List solution users
392         :param timeout: command timeout in secs
393         :param json: Enable JSON output
394         :return: SSO users
395         """
396         kwargs.clear()
397 
398         cmd_args = ['sso.user.ls']
399         if solution:
400             cmd_args.append('-s')
401 
402         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
403 
404     def ssouser_rm(self, name, timeout=15, json=False, **kwargs):
405         """
406         Remove SSO user.
407 
408         :param name: SSO user name
409         :param timeout: command timeout in secs
410         :param json: Enable JSON output
411         :return: SSO user and group IDs
412         :return: Command output
413         """
414         kwargs.clear()
415 
416         cmd_args = ['sso.user.rm', name]
417 
418         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
419 
420     def ssouser_update(self, name, password=None, description=None,
421                        solution=False, act_as_user=True, role=None, certificate=None,
422                        timeout=15, json=False, **kwargs):
423         """
424         Update SSO user.
425 
426         :param name: SSO user name
427         :param password: SSO person user password
428         :param description: SSO user description
429         :param solution: Whether is solution user
430         :param act_as_user: ActAsUser role for solution user WSTrust
431         :param role: Role for solution user (RegularUser|Administrator)
432         :param certificate: Certificate for solution user
433         :param timeout: command timeout in secs
434         :param json: Enable JSON output
435         :return: Command output
436         """
437         kwargs.clear()
438 
439         cmd_args = ['sso.user.update']
440         if description is not None:
441             cmd_args.extend(['-d', description])
442         if solution:
443             if act_as_user:
444                 cmd_args.append('-A')
445             if role is not None:
446                 assert role in ['RegularUser', 'Administrator'], (
447                     'Invalid solution user role: {}!'.format(role))
448                 cmd_args.extend(['-R', role])
449             if certificate is not None:
450                 cmd_args.extend(['-C', '{}'.format(certificate)])
451         else:
452             if password is not None:
453                 cmd_args.extend(['-p', password])
454         cmd_args.append(name)
455 
456         return self._run_govc_cli(cmd_args, timeout=timeout, json=json)
457 
458     def get_sso_user_privileges(self, username, timeout=15, **kwargs):
459         """
460         Get privileges associated with an SSO user
461         :param name: SSO user name
462         :param timeout: command timeout in secs
463         :return: List of privileges
464         """
465         kwargs.clear()
466 
467         permission_cmd_args = ['permissions.ls']
468         permission_output = self._run_govc_cli(permission_cmd_args, timeout=timeout)
469         privileges_list = []
470         normalized_user = username.lower()
471         for line in permission_output.splitlines()[1:]:
472             line_split = line.split('/') #formatted [role, entity, username, propogate]
473             user = line_split[1].split()[0].lower() # user formatted either <user> or <domain>\\<user>
474             user_split = user.split("\\")
475             role = line_split[0].strip()
476             constructed_user = user_split[1] + "@" + user_split[0] if len(user_split) == 2 else user
477             if normalized_user == constructed_user:
478                 role_cmd_args = ['role.ls', role]
479                 privileges_list.extend(self._run_govc_cli(role_cmd_args, timeout=timeout).split())
480                 break
481 
482         return privileges_list
483 
484     def set_sso_user_privileges(self, username, role, entity='/', timeout=15, **kwargs):
485         """
486         Attach a role to a user
487         :param username: SSO user name
488         :param role: Role to attach to SSO user
489         :param entity: Entity to attach privileges on, default root
490         :param timeout: command timeout in secs
491         """
492         kwargs.clear()
493 
494         permission_cmd_args = ["permissions.set"]
495         permission_cmd_args.extend(['-principal', username])
496         permission_cmd_args.extend(['-role', role])
497         permission_cmd_args.extend(['-propagate=true'])
498         permission_cmd_args.extend([entity])
499         self._run_govc_cli(permission_cmd_args, timeout=timeout)
500 
501     def role_ls(self, role=None, json=False, timeout=15, **kwargs):
502         """
503         Get information about roles. Lists names of all roles if a role is
504         not provided.
505         :param role: name of role to get information about
506         :param timeout: command timeout in secs
507         """
508         kwargs.clear()
509 
510         role_cmd_args = ["role.ls"]
511         if role:
512              role_cmd_args.append(role)
513         return self._run_govc_cli(role_cmd_args, timeout=timeout, json=json)
514 
515     def role_create(self, role, privileges, timeout=15, **kwargs):
516         """
517         Get information about roles. Lists names of all roles if a role is
518         not provided.
519         :param role: name of role to get information about
520         :param privileges: list of privileges to attach to role
521         :param timeout: command timeout in secs
522         """
523         kwargs.clear()
524 
525         role_cmd_args = ["role.create"]
526         role_cmd_args.append(role)
527         role_cmd_args.extend(privileges)
528         return self._run_govc_cli(role_cmd_args, timeout=timeout)
529 
530     def role_update(self, role, privileges_to_add=None, privileges_to_remove=None, timeout=15, **kwargs):
531         """
532         Get information about roles. Lists names of all roles if a role is
533         not provided.
534         :param role: name of role to get information about
535         :param privileges_to_add: list of privileges to attach to role
536         :param privileges_to_remove: list of privileges to remove
537         :param timeout: command timeout in secs
538         """
539         kwargs.clear()
540 
541         role_cmd_args = ["role.update"]
542         if privileges_to_add:
543             role_cmd_args.append("-a")
544             role_cmd_args.append(role)
545             role_cmd_args.extend(privileges_to_add)
546         if privileges_to_remove:
547             role_cmd_args.append("-r")
548             role_cmd_args.append(role)
549             role_cmd_args.extend(privileges_to_remove)
550         return self._run_govc_cli(role_cmd_args, timeout=timeout)
551 
552 class vcdr_create_vcenter_user(object):
553     def _get_password(self, account=None, retype=False):
554         '''
555         Prompt for a password.
556         '''
557         prompt = 'Retype password' if retype else 'Password'
558         if account is not None:
559             prompt += ' for account %s' % account
560         prompt += ': '
561         while True:
562             password = getpass(prompt=prompt)
563             if password:
564                 return password
565 
566     def _get_username_and_domain(self, user):
567         username = user
568         domain = None
569         if '@' in user:
570             username, domain = user.split('@', 1)
571         return username, domain
572 
573     def _get_user(self, govc_channel, user):
574         username, domain = self._get_username_and_domain(user)
575         users = govc_channel.ssouser_ls(json=True)
576         if users:
577             users = json.loads(users)
578             for user in users:
579                 if user['Id']['Name'] == username and user['Id']['Domain'] == domain:
580                     return user
581         return None
582 
583     def _get_role(self, govc_channel, role_name):
584         try:
585             role = govc_channel.role_ls(role_name, json=True)
586             return json.loads(role) if role else None
587         except:
588             # Unable to find role.
589             pass
590         return None
591 
592     def handle_vcdr_create_vcenter_user(self, vcenter, adminUsername, newUsername, adminPassword=None, newPassword=None, vcenterRole=None,
593                                        vcenterEntity=None, snapshotPrivs=False, failbackPrivs=True, keepExistingPrivs=False, createRoleOnly=False):
594         """
595         @param vcenter: vCenter IP to create new user on
596         @param adminUsername: Admin username at desired vCenter
597         @param newUsername: New username to be created in the desired vCenter
598         @param adminPassword: Admin password at desired vCenter
599         @param newPassword: New password to be attached to the new user in the desired vCenter
600         @param vcenterRole: Name of the vCenter role to attach to the new user
601         @param vcenterEntity: VCenter entity path to give the new user privileges on, by default root
602         @param snapshotPrivs: Associate privileges necessary for snapshotting and failover with the role
603         @param failbackPrivs: Associate privileges necessary for snapshotting, failover, and failback with the role
604         @param keepExistingPrivs: Keep existing privileges on provided role (i.e., do not prune unnecessary privileges from provided role)
605         @param createRoleOnly: Create role with necessary privileges but do not create a user account or assign permissions
606         """
607         try:
608             if adminPassword is None:
609                 adminPassword = self._get_password(account=adminUsername)
610             if newPassword is None:
611                 while True:
612                     newPassword = self._get_password(account=newUsername)
613                     newPassword2 = self._get_password(account=newUsername, retype=True)
614                     if newPassword == newPassword2:
615                         break
616                     print('Passwords do not match.')
617             govc_channel = GOVC(vcenter, adminUsername, adminPassword, exit_on_cmd_failure=False)
618         except:
619             msg = u'Unable to establish connection with vCenter {} with username {}'.format(vcenter, adminUsername)
620             logging.exception(msg)
621             print(msg)
622             return
623 
624         # Decide which privileges we care about. TODO: select based on CLI.
625         if failbackPrivs:
626             target_privs = VCDR_FAILBACK_PRIVS
627         else:
628             target_privs = VCDR_SNAPSHOT_PRIVS
629 
630         # Check the privileges of the Admin, and assign only the permissible privileges.
631         admin_role = self._get_role(govc_channel, 'Admin')
632         target_privs = list(set(admin_role['Privilege']) & set(target_privs + VCDR_LWD_PRIVS))
633 
634         # Create user if it doesn't already exist.
635         if not createRoleOnly:
636             existing_user = False
637             try:
638                 existing_user = self._get_user(govc_channel, newUsername)
639                 newUsernameWithOutDomain, _ = self._get_username_and_domain(newUsername)
640                 if not existing_user:
641                     govc_channel.ssouser_create(newUsernameWithOutDomain, newPassword)
642                 else:
643                     govc_channel.ssouser_update(newUsernameWithOutDomain, newPassword)
644             except:
645                 msg = u'Unable to {} user {} on vCenter {}'.format("update" if existing_user else "create", newUsername, vcenter)
646                 logging.exception(msg)
647                 print(msg)
648                 return
649 
650         # Create role if it doesn't already exist and add appropriate privs.
651         try:
652             existing_role = self._get_role(govc_channel, vcenterRole)
653             if existing_role:
654                 govc_channel.role_update(vcenterRole, privileges_to_add=target_privs)
655             else:
656                 govc_channel.role_create(vcenterRole, target_privs)
657         except:
658             msg = u'Unable to create/update role {} on vCenter {}'.format(vcenterRole, vcenter)
659             logging.exception(msg)
660             print(msg)
661             return
662 
663         # Remove extraneous privs from role.
664         if not keepExistingPrivs:
665             try:
666                 role = self._get_role(govc_channel, vcenterRole)
667                 privs = role['Privilege']
668                 extra_privs = list(set(privs) - set(target_privs))
669                 if extra_privs:
670                     govc_channel.role_update(vcenterRole, privileges_to_remove=extra_privs)
671             except:
672                 msg = u'Unable to remove extraneous privs from role {} on vCenter {}'.format(vcenterRole, vcenter)
673                 logging.exception(msg)
674                 print(msg)
675                 return
676 
677         # Update the permissions.
678         if not createRoleOnly:
679             try:
680                 vcenterEntity = vcenterEntity or '/'
681                 govc_channel.set_sso_user_privileges(newUsername, vcenterRole, vcenterEntity)
682             except:
683                 msg = u'Unable to create permission on {} for {}/{} on vCenter {}'.format(vcenterEntity, newUsername, vcenterRole, vcenter)
684                 logging.exception(msg)
685                 print(msg)
686                 return
687 
688         return
689 
690 
691 if __name__ == '__main__':
692     import argparse
693 
694     logging.basicConfig(level=logging.DEBUG, filename=LOG_FILE, filemode='a')
695     logging.info('Starting script')
696 
697     parser = argparse.ArgumentParser(description='Create vCenter user/role/permission with minimal privileges for use with VCDR.')
698     parser.add_argument('--vcenter',
699                         required=True,
700                         help='vCenter IP on which to create/update user')
701     parser.add_argument('--admin-username',
702                         required=True,
703                         metavar='admin-username',
704                         help='Admin username at desired vCenter')
705     parser.add_argument('--new-username',
706                         required=True,
707                         metavar='new-username',
708                         help='New username to be created in the desired vCenter')
709     parser.add_argument('--admin-password',
710                         metavar='admin-password',
711                         help='Admin password at desired vCenter (will be prompted if not provided)')
712     parser.add_argument('--new-password',
713                         metavar='new-password',
714                         help='New password for the new user in the desired vCenter (will be prompted if not provided)')
715     parser.add_argument('--vcenter-role',
716                         required=True,
717                         metavar='vcenter-role',
718                         help='Name of new or existing vCenter role to associate to the new user')
719     parser.add_argument('--keep-existing-privs',
720                         action='store_true',
721                         help='Keep existing privileges on provided role (i.e., do not prune unnecessary privileges from provided role)')
722     parser.add_argument('--create-role-only',
723                         action='store_true',
724                         help='Create role with necessary privileges but do not create a user account or assign permissions')
725 
726     group = parser.add_mutually_exclusive_group()
727     group.add_argument('--snapshot-privs',
728                        action='store_true',
729                        help='Create a user with privileges necessary to snapshot VMs and failover (the default)')
730     group.add_argument('--failback-privs',
731                         action='store_true',
732                         help='Create a user with privileges necessary to snapshot VMs, failover, and failback')
733 
734     args = vars(parser.parse_args())
735 
736     vcenter = args.get('vcenter')
737     admin_username = args.get('admin_username')
738     new_username = args.get('new_username')
739     admin_password = args.get('admin_password')
740     new_password = args.get('new_password')
741     vcenter_role = args.get('vcenter_role')
742     vcenter_entity = '/'
743     snapshot_privs = args.get('snapshot_privs')
744     failback_privs = args.get('failback_privs')
745     keep_existing_privs= args.get('keep_existing_privs', False)
746     create_role_only = args.get('create_role_only', False)
747 
748     worker = vcdr_create_vcenter_user()
749     worker.handle_vcdr_create_vcenter_user(vcenter, admin_username, new_username, adminPassword=admin_password, newPassword=new_password,
750                                           vcenterRole=vcenter_role, vcenterEntity=vcenter_entity, snapshotPrivs=snapshot_privs,
751                                           failbackPrivs=failback_privs, keepExistingPrivs=keep_existing_privs, createRoleOnly=create_role_only)
752 
753     logging.info('Ending script')
754 
755     exit(0)