Recently I was asked by an administrator to help him automate the process of joining ESXi Hosts or as PowerCLI refers to them “VMhosts” to a domain and granting permissions for a domain user with PowerCLI.
In this post I am going to show you how this can be done with a few lines of code and also share my lessons learned throughout this process. Let’s assume that you have list of VMhost IP addresses and all VMhosts have the same local administrator credentials, you want to join them to a domain and grant permissions for a domain user or group account assigning it a specific role. In the script I assume the role exists on the VMHost but later I will show you how to create a custom role.
The Set-VMHostAuthentication cmdlet is used for joining a VMhost to a domain. You should have in mind that the full domain name must be specified on the Domain parameter of the cmdlet.
Get-VMHostAuthentication -VMHost <VMHost>| Set-VMHostAuthentication -Domain <domain fullname> -User <domain user name for authentication> -Password <password for authentication> -JoinDomain -Confirm:$false
The New-VIPermssion cmdlet is used for creating permissions for a specified user. The user is obtained with the Get-VIAccount cmdlet which has the ability to retrieve Domain user or group accounts when the VMHost is joined to a domain. In contrast to Set-VMHostAuthentication the Domain parameter of Get-VIAccount expects the domain alias instead of domain full name.
You should also be aware that if you don’t specify an Id filter to the Get-VIAccount cmdlet it returns the maximum 5000 results and for domains with many accounts this could be a constraint. So it is recommended to use Id filter of the cmdlet itself instead of applying filtering over its results.
To obtain domain user accounts use –User switch.
Get-VIAccount -Domain <domain alias> -User -Id <user name filter>
To obtain domain group accounts use –Group switch.
Get-VIAccount -Domain <domain alias> -Group -Id <group name filter>
To grant permissions you should specify account to Principal parameter returned by the Get-VIAccount cmdlet, role which can be obtained by Get-VIRole or specified by name and entity which in our case will be the VM host.
New-VIPermission -Principal <VIAccount> -Role <VIRole> -Entity <VMHost>
Here is the entire script with a lot of input parameters needed by the used cmdlets in the script but the script itself is not complex:
param (
[Parameter(Mandatory=$true, HelpMessage=“List of VM host IPs”)]
[ValidateNotNull()]
[string[]]
$vmHostIPs,
[Parameter(Mandatory=$true, HelpMessage=“VM Host User Name”)]
[ValidateNotNull()]
[string]
$vmHostUserName,
[Parameter(Mandatory=$true, HelpMessage=“VM Host Password”)]
[ValidateNotNull()]
[string]
$vmHostPassword,
[Parameter(Mandatory=$true,HelpMessage=“Domain full name, required for joining hosts.”)]
[ValidateNotNull()]
[string]
$domainlFullName,
[Parameter(Mandatory=$true,HelpMessage=“Domain alias, required for retrieving domain accounts.”)]
[ValidateNotNull()]
[string]
$domainAlias,
[Parameter(Mandatory=$true, HelpMessage=“User name for domain authentication”)]
[ValidateNotNull()]
[string]
$domainUser,
[Parameter(Mandatory=$true, HelpMessage=“Password for domain authentication”)]
[ValidateNotNull()]
[string]
$domainPassword,
[Parameter(Mandatory=$true, HelpMessage=“Domain user name for which permissions will be granted”)]
[ValidateNotNull()]
[string]
$userNameToGrantPermissions,
[Parameter(Mandatory=$true, HelpMessage=“The name of the role you will assign to the user”)]
[ValidateNotNull()]
[string]
$roleName
)
foreach ($vmHostIPin$vmHostIPs) {
# Establish connection to a VMHost
$vmHostConnection= Connect-VIServer-Server $vmHostIP -User $vmHostUserName -Password $vmHostPassword
try {
# Get VMHost instance
$vmHost= Get-VMHost -Server $vmHostConnection
# Join the VMHost to a domain
Get-VMHostAuthentication -VMHost $vmHost | Set-VMHostAuthentication -Domain $domainlFullName -User $domainUser -Password $domainPassword -JoinDomain -Confirm:$false
# Get a domain account
$viAccount= Get-VIAccount -Domain $domainAlias -User -Id $userNameToGrantPermissions
if (-not $viAccount) {
throw “VIAccount with Id ‘$userNameToGrantPermissions’ not found in domain ‘$domainAlias'”
}
# Get role to assign
$viRole= Get-VIRole -Name $roleName
if (-not $viRole) {
throw “VIRole with name ‘ $viRole’ not found.”
}
# Add permissions on VMHost
New-VIPermission -Principal $viAccount -Role $viRole -Entity $vmHost
} catch {
Write-Error (“The following error has occurred for VMHost ‘$vmHost’: r
n”+$_)
} finally {
Disconnect-VIServer $vmHostConnection -Confirm:$false
}
}
As you can see the script opens a connection to each VMhost, joins it to the domain and creates permissions for a specific domain account. It relies on the existing role on the VMHost, but it can be easily modified to create a custom role and assign it to the obtained user.
To create a new custom role the New-VIRole cmdlet needs to be used specifying a name and list of privileges on its input. Here is an example:
New-VIRole -Name MyCustomRole -Privilege ‘Anonymous’, ‘View’, ‘Read’, ‘Power On’, ‘Power Off’
The script is calling the Get-VIAccount with a –User switch parameter which filters on domain user accounts. In order to retrieve a domain group account the –Group switch parameter should be used.
So the script looks pretty simple and straightforward but running it I’ve experienced the following problem. Sometimes Get-VIAccount failed to retrieve the domain user immediately after joining the VMhost to the domain and I received the following error “Error accessing directory: Can’t bind to LDAP server for domain: <DOMAIN>”.
It seems that synchronization with active directory needs some time after a host is joined to the domain and the problem is not 100% reproducible. So I solved it with a simple retry-wait mechanism on retrieving domain users.
# Get a domain account
$viAccount=$null
$retryCount= 5
while ((-not$viAccount) -and ($retryCount-ge 0)) {
try {
$viAccount= Get-VIAccount-Domain$domainAlias-User-Id$userNameToGrantPermissions
} catch {
Write-Error “Getting VIAccount with Id ‘$userNameToGrantPermissions’ failed with the following error: r
n $_”
Write-Host “Next attempt in 5 seconds”
Start-Sleep -Seconds 5
}
$retryCount—
}
Conclusion
In conclusion here are the lessons learned from this task:
- Get-VIAccount requires to specify the domain alias to Domain parameter
- Get-VIAccount limits the results to 5000
- Sometimes Get-VIAccount fails to obtain domain users immediately after a VMhost is joined to a domain.