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)