Write-Host "
Full credit to Edgar Sanchez @edmsanchez13
https://virtualcornerstone.com/2018/04/09/use-powercli-to-manage-users-on-an-esxi-host/
- Tested ESXi 6.0 / 6.5 / 6.7 host in a 6.5 / 6.7 VCSA
- ESXi 5.5 fails
- The script will change the root password on a single ESXi host in a connected state in a vCenter. Password complexity applies!
#1 - The first popup window will be for the vCenter login account.
#2 - The second popup window will have the ESXi root username filled in. Enter the new password
"
$vCenter = Read-Host "Enter the vCenter FQDN"
$thehost = Read-Host "Enter the ESXi hostname in $vCenter"
# Connect to your vCenter
Connect-VIServer $vCenter
function Set-ESXiAccount {
<#
.SYNOPSIS
Set ESXi 6.x account security settings
.DESCRIPTION
Updates ESXi local account Description, Password or Permission
.NOTES
Author: Edgar Sanchez - @edmsanchez13
.Link
https://github.com/edmsanchez/PowerShell
https://virtualcornerstone.com/
.INPUTS
No inputs required
.OUTPUTS
To Screen
.PARAMETER Name
User name (UserID) to update
.Example
Set-ESXiAccount -Name "testuser"
.PARAMETER Description
User ID Description to update
.EXAMPLE
Set-ESXiAccount -Name "testuser" -Description "Local test User"
.PARAMETER Permission
Permission to assign the User ID (Admin,ReadOnly,NoAccess)
.Example
Set-ESXiAccount -Name "testuser" -Permission "Admin" -VMhost devvm001.lab.local
.PARAMETER ResetPassword
Switch to reset the User ID's Password. This is a switch ONLY, You will be prompted for the Password during the script execution
.EXAMPLE
Set-ESXiAccount -Name "testuser" -ResetPassword -VMhost devvm001.lab.local
.PARAMETER VMhost
The name(s) of the vSphere ESXi Host(s)
.EXAMPLE
Set-ESXiAccount -Name "testuser" -ResetPassword -VMhost devvm001.lab.local
.PARAMETER Cluster
The name(s) of the vSphere Cluster(s)
.EXAMPLE
Set-ESXiAccount -Name "testuser" -Permission "Admin" -Cluster production-cluster
.PARAMETER Datacenter
The name(s) of the vSphere Virtual Datacenter(s).
.EXAMPLE
Set-ESXiAccount -Name "testuser" -Description "Local test User" -Permission "Admin" -Datacenter vDC001
#>
<#
----------------------------------------------------------[Declarations]----------------------------------------------------------
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)][String]$Name,
[Parameter(Mandatory = $false)][String]$Description,
[Parameter(Mandatory = $false)][ValidateSet("Admin", "ReadOnly", "NoAccess")]$Permission,
[switch]$ResetPassword,
[Parameter(Mandatory = $false)]$VMhost,
[Parameter(Mandatory = $false)]$Cluster,
[Parameter(Mandatory = $false)]$Datacenter
)
$skipCollection = @()
$outputCollection = @()
$vHostList = @()
$date = Get-Date -format s
$date = $date -replace ":", "-"
<#
----------------------------------------------------------[Execution]----------------------------------------------------------
#>
<#
Query PowerCLI version if
running Verbose
#>
if ($VerbosePreference -eq "continue") {
Write-Verbose -Message ((Get-Date -Format G) + "`tPowerCLI Version:")
Get-Module -Name VMware.* | Select-Object -Property Name, Version | Format-Table -AutoSize
} #END if
<#
Validate if a parameter was specified (-VMhost, -Cluster, or -Datacenter)
Although all 3 can be specified, only the first one is used
Example: -VMhost "host001" -Cluster "test-cluster". -VMhost is the first parameter
and what will be used.
#>
Write-Verbose -Message ((Get-Date -Format G) + "`tValidate parameters used")
if ([string]::IsNullOrWhiteSpace($VMhost) -and [string]::IsNullOrWhiteSpace($Cluster) -and [string]::IsNullOrWhiteSpace($Datacenter)) {
Write-Error -Message "You must specify a parameter (-VMhost, -Cluster, or -Datacenter)."
break
} #END if
if ($Description -or $Permission -or $ResetPassword) {
Write-Verbose -Message ((Get-Date -Format G) + "`tA Cmdlet Paramter was used")
}
else {
Write-Error -Message "You must specify a Cmdlet parameter to update (-Description, -Permission, or -ResetPassword)."
break
} #END if/else
<#
Check for an active connection to a ViServer
#>
Write-Verbose -Message ((Get-Date -Format G) + "`tValidate connection to a vSphere server")
if ($Global:DefaultViServers.Count -gt 0) {
Write-Host "`tConnected to $Global:DefaultViServers" -ForegroundColor Green
}
else {
Write-Error -Message "You must be connected to a vSphere server before running this Cmdlet."
break
} #END if/else
<#
Gather host list based on parameter used
#>
Write-Verbose -Message ((Get-Date -Format G) + "`tGather host list")
if ([string]::IsNullOrWhiteSpace($VMhost)) {
Write-Verbose -Message ((Get-Date -Format G) + "`t-VMhost parameter is Null or Empty")
if ([string]::IsNullOrWhiteSpace($Cluster)) {
Write-Verbose -Message ((Get-Date -Format G) + "`t-Cluster parameter is Null or Empty")
if ([string]::IsNullOrWhiteSpace($Datacenter)) {
Write-Verbose -Message ((Get-Date -Format G) + "`t-Datacenter parameter is Null or Empty")
}
else {
Write-Verbose -Message ((Get-Date -Format G) + "`tExecuting Cmdlet using Datacenter parameter")
Write-Host "`tGathering host list from the following DataCenter(s): " (@($Datacenter) -join ',')
foreach ($vDCname in $Datacenter) {
$tempList = Get-Datacenter -Name $vDCname.Trim() -ErrorAction SilentlyContinue | Get-VMHost
if ($tempList) {
$vHostList += $tempList | Sort-Object -Property Name
}
else {
Write-Warning -Message "`tDatacenter with name $vDCname was not found in $Global:DefaultViServers"
} #END if/else
} #END foreach
} #END if/else
}
else {
Write-Verbose -Message ((Get-Date -Format G) + "`tExecuting Cmdlet using cluster parameter")
Write-Host "`tGathering host list from the following Cluster(s): " (@($Cluster) -join ',')
foreach ($vClusterName in $Cluster) {
$tempList = Get-Cluster -Name $vClusterName.Trim() -ErrorAction SilentlyContinue | Get-VMHost
if ($tempList) {
$vHostList += $tempList | Sort-Object -Property Name
}
else {
Write-Warning -Message "`tCluster with name $vClusterName was not found in $Global:DefaultViServers"
} #END if/else
} #END foreach
} #END if/else
}
else {
Write-Verbose -Message ((Get-Date -Format G) + "`tExecuting Cmdlet using VMhost parameter")
Write-Host "`tGathering host list..."
foreach ($invidualHost in $VMhost) {
$tempList = Get-VMHost -Name $invidualHost.Trim() -ErrorAction SilentlyContinue
if ($tempList) {
$vHostList += $tempList | Sort-Object -Property Name
}
else {
Write-Warning -Message "`tESXi host $invidualHost was not found in $Global:DefaultViServers"
} #END if/else
} #END foreach
} #END if/else
$tempList = $null
<#
Main code execution
#>
$credentials = $null
foreach ($vmhost in $vHostList) {
<#
Skip if ESXi host is not in a Connected
or Maintenance ConnectionState
#>
Write-Verbose -Message ((Get-Date -Format G) + "`t$vmhost Connection State: " + $vmhost.ConnectionState)
if ($vmhost.ConnectionState -eq "Connected" -or $vmhost.ConnectionState -eq "Maintenance") {
<#
Do nothing - ESXi host is reachable
#>
}
else {
<#
Use a custom object to keep track of skipped
hosts and continue to the next foreach loop
#>
$skipCollection += [pscustomobject]@{
'Hostname' = $vmhost.Name
'Connection State' = $vmhost.ConnectionState
} #END [PSCustomObject]
continue
} #END if/else
<#
Validate ESXi Version
Will run only of 6.x
#>
if ($vmhost.ApiVersion -notmatch '6.') {
Write-Warning -Message ("`t$vmhost is ESXi v" + $vmhost.ApiVersion + ". Skipping host.")
continue
}
<#
Validate that Account to update exists
#>
$esxcli = Get-EsxCli -VMHost $vmhost -V2
Write-Verbose -Message ((Get-Date -Format G) + "`tValidating $Name User ID exists on $vmhost...")
if ($esxcli.system.account.list.Invoke() | Where-Object {$_.UserID -eq $Name.Trim()}) {
$accountArgs = $esxcli.system.account.set.CreateArgs()
$updateAccount = $false
$updatePermission = $false
<#
Update ESXi Local Account
Query for Pasword if -ResetPassword switch is used
#>
if ($Description) {
$updateAccount = $true
$accountArgs.id = $Name.Trim()
$accountArgs.description = $Description.Trim()
} #END if
if ($ResetPassword) {
if ($credentials) {
<#
Do nothing - Credentials already Gathered
#>
}
else {
Write-Verbose -Message ((Get-Date -Format G) + "`tPrompt for $Name password...")
$credentials = Get-Credential -UserName $Name -Message "Enter new password for UserID: $Name"
} #END if/else
$updateAccount = $true
$accountArgs.id = $Name.Trim()
try {
$accountArgs.password = $credentials.GetNetworkCredential().Password
$accountArgs.passwordconfirmation = $credentials.GetNetworkCredential().Password
}
catch {
Write-Host "`nError: Failed to gather User ID: $Name password" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
break
} #END catch
} #END if
if ($updateAccount) {
Write-Host "`tUpdating UserID: $Name on $vmhost..."
try {
$esxcli.system.account.set.Invoke($accountArgs)
}
catch {
$vmhostAdvancedView = Get-View $vmhost.ExtensionData.ConfigManager.AdvancedOption
$pwdQualityControl = $vmhostAdvancedView.Setting | Where-Object {$_.Key -eq "Security.PasswordQualityControl"} | Select-Object -ExpandProperty Value
$retry = ($pwdQualityControl.Split('')[0])
$pwd = ($pwdQualityControl.Split('')[1]).Split('=')[1]
Write-Host "`nError: Failed to update User ID: $Name" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host "`nPassword Quality Control: " $pwdQualityControl -ForegroundColor Green
Write-Host $retry ": is the number of times a user is prompted for a new password if the password candidate is not sufficiently strong."
Write-Host "N0 =" $pwd.Split(',')[0] ": is the number of characters required for a password that uses characters from only one character class. For example, the password contains only lowercase letters."
Write-Host "N1 =" $pwd.Split(',')[1] ": is the number of characters required for a password that uses characters from two character classes."
Write-Host "N2 =" $pwd.Split(',')[2] ": is used for passphrases. ESXi requires three words for a passphrase. Each word in the passphrase must be 8-40 characters long."
Write-Host "N3 =" $pwd.Split(',')[3] ": is the number of characters required for a password that uses characters from three character classes."
Write-Host "N4 =" $pwd.Split(',')[4] ": is the number of characters required for a password that uses characters from all four character classes."
break
} #END try
} #END if
<#
Update Permission if parameter was specified
#>
if ($Permission) {
Write-Host "`tUpdating Permission for UserID: $Name on $vmhost..."
$updatePermission = $true
$permissionArgs = $esxcli.system.permission.set.CreateArgs()
$permissionArgs.id = $Name.Trim()
$permissionArgs.group = $false
$permissionArgs.role = $Permission.Trim()
$esxcli.system.permission.set.Invoke($permissionArgs)
} #END if
<#
List account added
#>
$accountUpdated = $esxcli.system.account.list.Invoke() | Where-Object {$_.UserID -eq $Name.Trim()}
$accountRole = $esxcli.system.permission.list.Invoke() | Where-Object {$_.Principal -eq $Name.Trim()} | Select-Object -ExpandProperty Role
<#
Use a custom object to store
collected data
#>
$outputCollection += [PSCustomObject]@{
'Hostname' = $vmhost.Name
'UserID' = $accountUpdated.UserID
'Role' = $accountRole
'Description' = $accountUpdated.Description
} #END [PSCustomObject]
}
else {
Write-Warning -Message "`t$Name User ID does not exist $vmhost. Skipping host."
continue
} #END if/else
} #END foreach
<#
Display skipped hosts and their connection status
#>
If ($skipCollection) {
Write-Warning -Message "`tCheck Connection State or Host name"
Write-Warning -Message "`tSkipped hosts:"
$skipCollection | Format-Table -AutoSize
} #END if
<#
Validate output arrays
#>
if ($outputCollection) {
Write-Verbose -Message ((Get-Date -Format G) + "`tAccount Updated")
Write-Host "`nESXi Local Account Updated:" -ForegroundColor Green
$outputCollection | Format-Table -Wrap
}
else {
Write-Verbose -Message ((Get-Date -Format G) + "`tNo Account Updated")
} #END if/else
} #END function
# the one liner
Set-ESXiAccount -Name "root" -ResetPassword -VMhost $thehost
# Disconnect from the vCenter
Disconnect-VIServer $vCenter -Confirm:$false