Shutdown/Power up a vSAN cluster with PowerCli

I have been doing a lot of lab testing lately and using  vCloud Director is a great way to be able to run side by side tests (sometimes destructive!) against multiple environments without requiring multiple physical clusters. I wanted to emulate a Dell EMC VxRail appliance so I created a 4 node nested vSAN cluster with a vCenter appliance & external PSC running on the cluster, to use as a vCD template. When creating vCD templates it is preferable to power down the environment before adding to the vCD catalog. When it comes to powering down this environment, because the vCenter & PSC are running on the cluster, there is a chicken and egg scenario as you need to power down in this order:

  1. vCenter
  2. PSC
  3. Put ESXi hosts in maintenance mode (vSAN aware operation)
  4. Power down ESXi hosts

The inverse applies when powering up the environment.  So rather than connect to multiple different interfaces to execute the power down or power up operations I put together the following Menu based PowerCli script with the following Options

These options will call the following functions

Option 1. Shutdown vSAN Cluster & PSC/vCenter

  • ConnectViServer $vCenterFQDN $vCenterUser $vCenterPassword
    • Generic function to connect to a VIServer (ESXi or vCenter). Just pass the hostname, user & password. This instance connects to vCenter as we need to perform a DRS operation.
  • ChangeDRSLevel $PartiallyAutomated
    • The ChangeDRSLevel function takes an argument for the level to set it to. In this case it sets it to partially Automated to stop DRS from moving VMs around.
  • MoveVMs
    • This function will move the VMs defined in $VMList to the host defined in $VMHost. This ensures that we know where the VMs are when we come to power the cluster back up
  • ConnectViServer $VMHost $VMHostUser $VMHostPassword
    • Generic function to connect to a VIServer (ESXi or vCenter). Just pass the hostname, user & password. This instance connects to the host defined in the $VMHost variable
  • ShutdownVM
    • This function will gracefully shutdown the VMs in the order defined in $VMList. In this case it will shutdown the vCenter first & then the PSC. This list can be expanded to include other VMs or could be refactored to use a CSV for a large list of VMs
  • EnterMaintenanceMode
    • This function will put all hosts defined in $VMHosts into maintenance mode. As this is a vSAN cluster it passes the NoAction flag to prevent any data moment or rebuild
  • ShutdownESXiHosts
    • This function will shutdown all hosts in the cluster

Option 2. Startup vSAN Cluster & PSC/vCenter

  • ExitMaintenanceMode
    • This function will exit all hosts from maintenance mode
  • ConnectViServer $VMHost $VMHostUser $VMHostPassword
    • Generic function to connect to a VIServer (ESXi or vCenter). Just pass the hostname, user & password. This instance connects to the host defined in the $VMHost variable
  • StartVMs
    • This function will startup the VMs in the reverse order defined in $VMList. In this case it will startup the PSC first & then the vCenter.
  • PollvCenter
    • This function will Poll vCenter until it is up and available
  • ConnectViServer $vCenterFQDN $vCenterUser $vCenterPassword
    • Generic function to connect to a VIServer (ESXi or vCenter). Just pass the hostname, user & password. This instance connects to vCenter as we need to perform a DRS operation
  • ChangeDRSLevel $FullyAutomated
    • The ChangeDRSLevel function takes an argument for the level to set it to. In this case it sets it to Fully Automated as we are done with the maintenance.

The script is posted to Github here and is also posted below. This was written and tested against vSphere 6. I will update it in the coming weeks for vSphere 6.5 and hopefully leverage some of the new APIs

# Script to shutdown & startup a vSAN cluster when vCenter/PSC are running on the cluster
# Created by Brian O'Connell | Dell EMC
# Provided with zero warranty! Please test before using in anger!
# @LifeOfBrianOC
# https://lifeofbrianoc.com/

## User Variables ##
$vCenterFQDN = "vcs01.domain.local"
$vCenterUser = "VC_Admin@domain.local"
$vCenterPassword = "Password123!"
$Cluster = "MARVIN-Virtual-SAN-Cluster"
$VMList = @("VCS01", "PSC01")
$VMHosts = @("esxi04.domain.local", "esxi05.domain.local", "esxi06.domain.local", "esxi07.domain.local")
$VMHost = "esxi04.domain.local"
$VMHostUser = "root"
$VMHostPassword = "Password123!"

### DO NOT MODIFY ANYTHING BELOW THIS LINE ###

# Add Required PowerCli Modules
Get-Module -ListAvailable VM* | Import-Module

# Function to Connect to VI Host (vCenter or ESXi). Pass host, username & password to the function
Function ConnectVIServer ($VIHost, $User, $Password) {
    Write-Host " "
    Write-Host "Connecting to $VIHost..." -Foregroundcolor yellow
    Connect-VIServer $VIHost -User $User -Password $Password | Out-Null
	Write-Host "Connected to $VIHost..." -Foregroundcolor Green
    Write-Host "------------------------------------------------" -Foregroundcolor Green
}

# Define DRS Levels to stop Vms from moving away from the defined host
$PartiallyAutomated = "PartiallyAutomated"
$FullyAutomated = "FullyAutomated"

# Function to Change DRS Automation level						
Function ChangeDRSLevel ($Level) {						

    Write-Host " "
    Write-Host "Changing cluster DRS Automation Level to Partially Automated" -Foregroundcolor yellow
    Get-Cluster $cluster | Set-Cluster -DrsAutomation $Level -confirm:$false | Out-Null
    Write-Host "------------------------------------------------" -Foregroundcolor yellow
}
						
# Function to Move the Vms to a defined host so they can be easily found when starting back up
Function MoveVMs {

    Foreach ($VM in $VMList) {
        # Power down VM
        Write-Host " "
        Write-Host "Moving $VM to $VMHost" -Foregroundcolor yellow
        Get-VM $VM | Move-VM -Destination $VMHost -Confirm:$false | Out-Null
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
    }   
    Disconnect-VIServer $vCenterFQDN -confirm:$false | Out-Null
}

# Function to Shutdown VMs
Function ShutdownVM  {

    Foreach ($VM in $VMList) {
        # Power down VM
        Write-Host " "
        Write-Host "Shutting down $VM" -Foregroundcolor yellow
        Shutdown-VMGuest $VM -Confirm:$false | Out-Null
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "Waiting for $VM to be Shutdown" -Foregroundcolor yellow
        # Check VM powerstate and wait until it is powered off before proceeding with the next VM
        do {
            sleep 15
            $powerState = (get-vm $VM).PowerState
        }
        while ($powerState -eq "PoweredOn")
        Write-Host " "
        Write-Host "$VM Shutdown.." -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow	
    }
}

# Function to put all ESXi hosts into maintenance mode with the No Action flag for vSAN data rebuilds
Function EnterMaintenanceMode {

    Foreach ($VMHost in $VMHosts) {
        Connect-VIServer $VMHost -User root -Password $VMHostPassword | Out-Null
        # Put Host into Maintenance Mode
        Write-Host " "
        Write-Host "Putting $VMHost into Maintenance Mode" -Foregroundcolor yellow
        Get-View -ViewType HostSystem -Filter @{"Name" = $VMHost }|?{!$_.Runtime.InMaintenanceMode}|%{$_.EnterMaintenanceMode(0, $false, (new-object VMware.Vim.HostMaintenanceSpec -Property @{vsanMode=(new-object VMware.Vim.VsanHostDecommissionMode -Property @{objectAction=[VMware.Vim.VsanHostDecommissionModeObjectAction]::NoAction})}))}
        Disconnect-VIServer $VMHost -confirm:$false | Out-Null
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "$VMHost in maintenance mode.." -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow	
    }
}

# Function to Exit hosts from maintenance mode
Function ExitMaintenanceMode {

    Foreach ($VMHost in $VMHosts) {
        Connect-VIServer $VMHost -User root -Password $VMHostPassword | Out-Null
        # Exit Maintenance Mode
        Write-Host " "
        Write-Host "Exiting Maintenance Mode for $VMHost" -Foregroundcolor yellow
        Set-VMHost $VMHost -State "Connected" | Out-Null
        Disconnect-VIServer $VMHost -confirm:$false | Out-Null
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "$VMHost out of maintenance mode.." -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow	
    }
    Write-Host "Waiting for vSAN Cluster to be Online" -Foregroundcolor yellow	
	Sleep 60							
}

# Function to shutdown hosts
Function ShutdownESXiHosts {

    Foreach ($VMHost in $VMHosts) {
        # Exit Maintenance Mode
        Write-Host " "
        Write-Host "Shutting down ESXi Hosts" -Foregroundcolor yellow
        Connect-VIServer -Server $VMHost -User root -Password $VMHostPassword | %{
            Get-VMHost -Server $_ | %{
                $_.ExtensionData.ShutdownHost_Task($TRUE) | Out-Null
            }
        }
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "ESXi host $VMHost shutdown.." -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow	
    }
		Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "All ESXi Hosts shutdown.." -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
}

# Function to Start VMs in the reverse order they were powered down									
Function StartVMs {
    # Reverse the VM list to start in reverse order
    [array]::Reverse($VMList)

    Foreach ($VM in $VMList) {
        # Power on VM
        Write-Host " "
        Write-Host "Powering on $VM" -Foregroundcolor yellow
        Start-VM $VM -Confirm:$false | Out-Null
        Write-Host "------------------------------------------------" -Foregroundcolor yellow
        Write-Host " "
        Write-Host "Waiting for $VM to be Powered On" -Foregroundcolor yellow
        # Check VM powerstate and wait until it is powered on before proceeding with the next VM
        do {
            sleep 15
            $powerState = (get-vm $VM).PowerState
        }
        while ($VM -eq "PoweredOff")
        Write-Host " "
        Write-Host "$VM Powered On..proceeding with next VM" -Foregroundcolor green
        Write-Host "------------------------------------------------" -Foregroundcolor yellow	
    }

}

# Function to Poll the status of vCenter after starting up the VM
Function PollvCenter {

    do 
    {
        try 
        {
            Write-Host " "
            Write-Host "Polling vCenter $vCenterFQDN Availability...." -ForegroundColor Yellow
            Write-Host "------------------------------------------------" -Foregroundcolor yellow
            # Create Web Request
            [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
            $HTTP_Request = [System.Net.WebRequest]::Create("https://$($vCenterFQDN):9443")

            # Get a response
            $HTTP_Response = $HTTP_Request.GetResponse()

            # Get the HTTP code
            $HTTP_Status = [int]$HTTP_Response.StatusCode

            If ($HTTP_Status -eq 200) { 
                Write-Host " "
                Write-Host "vCenter $vCenterFQDN is Available!"  -ForegroundColor Green
                Write-Host "------------------------------------------------" -Foregroundcolor Green
                # Close HTTP request
                $HTTP_Response.Close()
            }
        }
        catch { 
            Write-Host " "
            Write-Host "vCenter $vCenterFQDN Not Available Yet...Retrying Poll..."  -ForegroundColor Cyan
            Write-Host "------------------------------------------------" -Foregroundcolor Cyan
    } }
    While ($HTTP_Status -ne 200)	
}

# Function to display the main menu 
Function Menu 
{
    Clear-Host         
    Do
    {
        Clear-Host                                                                        
        Write-Host -Object 'Please choose an option'
        Write-Host     -Object '**********************'	
        Write-Host -Object 'vCenter & vSAN Maintenance Options' -Foregroundcolor Yellow
        Write-Host     -Object '**********************'
        Write-Host -Object '1.  Shutdown vSAN Cluster & PSC/vCenter '
        Write-Host -Object ''
        Write-Host -Object '2.  Startup vSAN Cluster & PSC/vCenter '
        Write-Host -Object ''
        Write-Host -Object 'Q.  Exit'
        Write-Host -Object $errout
        $Menu = Read-Host -Prompt '(Enter 1 - 2 or Q to quit)'

        switch ($Menu) 
        {
            1 
            {
				ConnectVIServer $vCenterFQDN $vCenterUser $vCenterPassword
                ChangeDRSLevel $PartiallyAutomated
                MoveVMs
                ConnectVIServer $VMHost $VMHostUser $VMHostPassword
                ShutdownVM
                EnterMaintenanceMode
                ShutdownESXiHosts   
            }
            2 
            { 
				ExitMaintenanceMode
                ConnectVIServer $VMHost $VMHostUser $VMHostPassword
                StartVMs
				PollvCenter
                ConnectVIServer $vCenterFQDN $vCenterUser $vCenterPassword
                ChangeDRSLevel $FullyAutomated
            }
            Q 
            {
                Exit
            }	
            default 
            {
                $errout = 'Invalid option please try again........Try 1-2 or Q only'
            }

        }
    }
    until ($Menu -eq 'q')
}   

# Launch The Menu
Menu

Snapshot Management with PowerCli

I’ve been doing some lab work recently, testing out some new automation for deploying Dell EMC Enterprise Hybrid Cloud (EHC) and as part of the testing we needed to be able to quickly and easily create on the fly rollback points at incremental steps in the build to assist with troubleshooting (outside of the standard rollback points created by the EHC Automated Install Tool (AIT) ).

Note: AIT is currently an internal Dell EMC tool used by our professional services organisation to deploy and configure EHC on a customer site.

Disclaimer: As with all scripts and code that you find on the web you should thoroughly test this in a lab environment before considering to use it in production. This script comes with zero warranty as it was something that was created quickly for lab use only!

Snapshots were enough to give us a rollback point, so to achieve this I put together a menu based PowerCli script that will take snapshots of a defined list of VMs with a defined snapshot name, rollback to the last snap taken, or rollback to a defined snapshot.

For more information on how to add a menu to a PowerShell script go here, and for how to add a “Press any key to continue..” to a script go here.

Here is the script for snapshot management. It is broken up into the following PowerShell Functions:

  • CreateVMSnapshot
  • RevertLastVMSnapshot
  • RevertSpecificVMSnapshot
  • anyKey
  • Menu

Before running the script you need to edit the user variables for your environment. The $VMList variable is a comma separated list of VM names as they appear in vCenter. In my example these are the components of a distributed vRA deployment. The $SnapshotName vaiable will be used when creating snapshots or when executing the Revert To Specific Snapshot option

# User Variables
$vCenterFQDN = "vcs01.domain.local"
$vCenterUser = "administrator@vsphere.local"
$vCenterPassword = "Password123!"
$VMList = @("vra01", "vra02", "web01", "web02", "mgr01", "mgr02", "dem01", "dem02", "agt01", "agt02")
$SnapshotName = "Snap01"

To execute the script just browse to the directory you saved the script in a PowerShell or PowerCli console, run ./SnapshotManagement.ps1 and you will be presented with a menu

Select the desired option from the menu. The operations are running Async so are quite quick to complete.

The raw code is below. I’ve also posted the script to GitHub here

# User Variables
$vCenterFQDN = "vcs01.domain.local"
$vCenterUser = "administrator@vsphere.local"
$vCenterPassword = "Password123!"
$VMList = @("vra01", "vra02", "web01", "web02", "mgr01", "mgr02", "dem01", "dem02", "agt01", "agt02")
$SnapshotName = "Snap01"
###############################
# DO NOT EDIT BELOW THIS LINE #
###############################
# Add Required Snappins
Get-Module -ListAvailable VM* | Import-Module

Function CreateVMSnapshot {
# Connect to vCenter
Connect-VIServer $vCenterFQDN -username $vCenterUser -password $vCenterPassword
	Foreach ($VM in $VMList) {
	Write-Host "Creating Snapshot for $VM"
	New-Snapshot -VM $VM -Memory -quiesce -Name $SnapshotName -RunAsync
							 }							 
									}
									
Function RevertLastVMSnapshot {
# Connect to vCenter
Connect-VIServer $vCenterFQDN -username $vCenterUser -password $vCenterPassword
	Foreach ($VM in $VMList) {
	Write-Host "Reverting Snapshot for $VM"
	$snap = Get-Snapshot -VM $VM | Sort-Object -Property Created -Descending | Select -First 1
    Set-VM -VM $vm -SnapShot $snap -Confirm:$false  -RunAsync | Out-Null
							 }							 
									}

Function RevertSpecificVMSnapshot {
# Connect to vCenter
Connect-VIServer $vCenterFQDN -username $vCenterUser -password $vCenterPassword
	Foreach ($VM in $VMList) {
	Write-Host "Reverting Snapshot for $VM"
	#$snap = Get-Snapshot -VM $VM | Sort-Object -Property Created -Descending | Select -First 1
    Set-VM -VM $vm -SnapShot $SnapshotName -Confirm:$false  -RunAsync | Out-Null
							 }							 
									}									

Function anyKey 
{
    Write-Host -NoNewline -Object 'Press any key to return to the main menu...' -ForegroundColor Yellow
    $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
    Menu
}
									
Function Menu 
{
    Clear-Host         
    Do
    {
        Clear-Host                                                                        
        Write-Host -Object 'Please choose an option'
        Write-Host     -Object '**********************'	
        Write-Host -Object 'Snapshot VM Options' -ForegroundColor Yellow
        Write-Host     -Object '**********************'
        Write-Host -Object '1.  Snapshot VMs '
		Write-Host -Object ''
        Write-Host -Object '2.  Revert to Last Snapshot '
		Write-Host -Object ''
		Write-Host -Object '3.  Revert To Specific Snapshot '
		Write-Host -Object ''
        Write-Host -Object '4.  Exit'
        Write-Host -Object $errout
        $Menu = Read-Host -Prompt '(0-3)'

        switch ($Menu) 
        {
           1 
            {
                CreateVMSnapshot 			
                anyKey
            }
            2 
            {
                RevertLastVMSnapshot
                anyKey
            }
			3 
            {
                RevertSpecificVMSnapshot
                anyKey
            }
            4 
            {
                Exit
			}	
            default 
            {
                $errout = 'Invalid option please try again........Try 0-4 only'
            }

        }
    }
    until ($Menu -ne '')
}   

# Launch The Menu
Menu

Add “Press any key to continue..” to a PowerShell script

From time to time it is nice to have a “Press any key to continue..” break point in a script to allow the user to review the status of an operation or just to add a user interaction to acknowledge the completion of an operation. This is especially useful when using a menu based script (see here) where the script will revert back to the menu once an operation is complete making it difficult to see the status of an operation when it completes or any Write-Host messages that may have been displayed. To get around this I use the following PowerShell Function to insert a “Press any key to continue..” break point that will wait for the user to…you guessed it…press the any key! 🙂

I use then when using a PowerShell Menu (See more about that here). You can edit the text in the quotes on line 3 to suite your use case. In my case i am calling the Menu function on line 5 so that when a user presses a key it will revert to the script menu. Simples!


Function anyKey
{
Write-Host -NoNewline -Object 'Press any key to return to the main menu...' -ForegroundColor Yellow
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
Menu
}


Setup multiple vRA tenants using powershell and the vRA7 REST API

Following on from my post on Creating a local user in vRA7 using the REST API i wanted to try and script the entire process of creating multiple vRealize Automation 7 tenants as in our lab as we often need to spin up multiple tenants for testing or development purposes.

Some assumptions:

  • Each tenant has the same prefix of “dev-” and is appended with a 3 digit number starting at “001”
  • Each tenant gets the same local user created with matching credentials
  • Each tenant gets the same AD directory added
  • Each tenant gets the same AD groups added

This script will do the following:

  • Log into the default tenant
  • Create a new tenant
  • Create a local user for the tenant
  • Add the local user as a tenant & IaaS admin
  • Log into the new tenant as the local user
  • Setup identity store directories
  • Log back into the default tenant
  • Edit the new tenant
  • Add domain users/groups as tenant & IaaS admins
  • Log into the new tenant as a tenant and IaaS admin and start configuring the tenant

So as to avoid the requirement to edit the powershell script directly i put all configuration variables in an external .cfg file. This file needs to be placed in the same directory as the powershell script.

Firstly here is the config file contents. Edit each variable to match your environment. Modify the numberOfTenants variable to set the number of tenants you want to create. The example below will create 20 tenants.

[vRA FQDN]
VRA=vra-vip.domain.local

[vRA Credentials to acquire authentication token]
vRAUsername=administrator@vsphere.local
vRAPassword=Password123!
vRADefaultTenant=vsphere.local

[Create tenant details]
numberOfTenants=2
tenantIDPrefix=dev-
tenantURLPrefix=dev-
tenantNamePrefix=dev-
tenantDescription=DevelopmentTenant
tenantemailAddress=admin@vsphere.local

[Local Admin User Details]
firstName=vRA
lastName=Admin
emailAddress=vraadmin@vsphere.local
description=vRAAdmin
locked=false
disabled=false
password=Password123!
domain=vsphere.local
userName=vraadmin
name=vraadmin

[Tenant Directory Details]
adDomain=domain.local
adDomainalias=Domain
type=AD
adUserNameDn=cn=adbind_vra,OU=EHC,DC=domain,DC=local
adBindPassword=Password123!
adURL=ldap://domain.local:389
adGroupBaseSearchDn=ou=EHC,DC=domain,DC=local
adUserBaseSearchDn=ou=EHC,DC=domain,DC=local

[AD Domain Groups to add as Tenant & IaaS Admins]
tenantAdmins=EHC_Tenant_Admins@domain.local
tenantRoleID=CSP_TENANT_ADMIN
iaasAdmins=EHC_IaaS_Admins@domain.local
iaasRoleID=COM_VMWARE_IAAS_IAAS_ADMINISTRATOR

And here is the script to create the tenants. It is broken up into multiple functions

# Script to create vRA7 Tenants in bulk
# Ensure you update the associated cfg file
# with the details of your vRA environment
# and details of the tenants you wish to create
# Created by Brian O'Connell
# Version 1.0.0

# Import configuration variables from external cfg file
Get-Content createvRATenants.cfg | Foreach-Object{
if ($_.length -gt 0) {
 $var = $_ -Split '=',2
 New-Variable -Name $var[0] -Value $var[1]
 }
 } 

Function getvRAAuthToken {
# Construct credentials from config file
$credentials=@{username=$vRAUsername;password=$vRAPassword;tenant=$vRADefaultTenant}
############# Get Auth token ###############
$headers=@{
 "Accept"="application/json"
}
$Global:token = Invoke-RestMethod -Uri "https://$($VRA)/identity/api/tokens" -Method Post -Headers $headers -ContentType application/json -Body (ConvertTo-Json $credentials) | Select -ExpandProperty id
Write-Host "vRA Authentication Token Acquired" -ForegroundColor Green
 } 

Function createvRATenant {
 # ############ Create Tenant ###############
$headers = @{"Accept" = "application/json"}
$headers.Add("Authorization", "Bearer $token")

#Create the Tenant
for ($firstTenantNumber=1; $firstTenantNumber -le $numberOfTenants; $firstTenantNumber++)
{
 New-Variable -Name "var$firstTenantNumber" -Value $firstTenantNumber
 $tenantNumber = $firstTenantNumber.ToString("000")
$tenantid = -join ($tenantIDPrefix,$tenantNumber)
$tenantURL = -join ($tenantURLPrefix,$tenantNumber)
$tenantName = -join ($tenantNamePrefix,$tenantNumber)
$tenantBody= @"
{
 "@type": "Tenant",
 "id": "$tenantid",
 "urlName": "$tenantURL",
 "name": "$tenantName",
 "description": "$tenantDescription",
 "contactEmail": "$tenantemailAddress"
}
"@ 

$createTenant = Invoke-RestMethod -Method PUT -URI "https://$($VRA)/identity/api/tenants/$($tenantID)" -headers $headers -ContentType application/json -body $tenantBody
Write-Host "Tenant $($tenantName) created successfully" -ForegroundColor Green
}
 }

Function createvRALocalAdminUser {
 ############# Create Local Admin User ###############

$headers = @{"Accept" = "application/json"}
$headers.Add("Authorization", "Bearer $token")
$userBody= @"
{ "@type": "User",
 "firstName": "$firstName",
 "lastName": "$lastName",
 "emailAddress": "$emailAddress",
 "description": "$description",
 "locked": false,
 "disabled": false,
 "password": "$password",
 "domain": "$domain",
 "userName": "$userName",
 "principalId": {
 "domain": "$domain",
 "name": "$name"
 }
}
"@

for ($firstTenantNumber=1; $firstTenantNumber -le $numberOfTenants; $firstTenantNumber++)
{
 New-Variable -Name "var$firstTenantNumber" -Value $firstTenantNumber
 $tenantNumber = $firstTenantNumber.ToString("000")
 $tenantid = -join ($tenantIDPrefix,$tenantNumber)
#Create the user
$createUser = Invoke-RestMethod -Method Post -URI "https://$($VRA)/identity/api/tenants/$($tenantID)/principals" -headers $headers -ContentType "application/json" -body $userBody
Write-Host "Local Admin User for tenant $($tenantid) created successfully" -ForegroundColor Green
} 

 }

Function updatevRALocalAdminUserRoles {
 ############# Add Local Admin User to Tenant & IaaS Admin groups ###############

$headers = @{"Accept" = "application/json"}
$headers.Add("Authorization", "Bearer $token")
$principal = "vraadmin@vsphere.local"
$roleIDs = @("CSP_TENANT_ADMIN","COM_VMWARE_IAAS_IAAS_ADMINISTRATOR")

for ($firstTenantNumber=1; $firstTenantNumber -le $numberOfTenants; $firstTenantNumber++)
{
 New-Variable -Name "var$firstTenantNumber" -Value $firstTenantNumber
 $tenantNumber = $firstTenantNumber.ToString("000")
 $tenantid = -join ($tenantIDPrefix,$tenantNumber)
#Add the user to tenant & IaaS admins
foreach ($roleID in $roleIDs) {
$makeUserAdmin = Invoke-RestMethod -Method PUT -URI "https://$($VRA)/identity/api/authorization/tenants/$($tenantID)/principals/$($principal)/roles/$($roleID)" -headers $headers -body "{}"
}
Write-Host "Local Admin User Added to Tenant & IaaS Admins for tenant $($tenantid) " -ForegroundColor Green
 }
 }

Function createvRATenantDirectory {
 ############# Add AD Tenant directory ###############
$headers = @{"Accept" = "application/json"}
$headers.Add("Authorization", "Bearer $token")

$directoryBody= @"
{"@type": "IdentityStore",
"domain": "$adDomain",
"name": "$adDomain",
"alias": "$adDomainalias",
"type": "$type",
"userNameDn": "$adUserNameDn",
"password": "$adBindPassword",
"url": "$adURL",
"groupBaseSearchDn": "$adGroupBaseSearchDn",
"userBaseSearchDn": "$adUserBaseSearchDn"
}
"@
for ($firstTenantNumber=1; $firstTenantNumber -le $numberOfTenants; $firstTenantNumber++)
{
 New-Variable -Name "var$firstTenantNumber" -Value $firstTenantNumber
 $tenantNumber = $firstTenantNumber.ToString("000")
 $tenantid = -join ($tenantIDPrefix,$tenantNumber)
#Create the directory
$createDirectory = Invoke-RestMethod -Method Post -URI "https://$($VRA)/identity/api/tenants/$($tenantID)/directories" -headers $headers -ContentType "application/json" -body $directoryBody
Write-Host "Tenant Directory Created for tenant $($tenantid) " -ForegroundColor Green
}

 }

Function addDomainGroupstovRAAdmins {
############## Add AD Domain Groups to vRA Tenant & IaaS Admin groups ###############

$headers = @{"Accept" = "application/json"}
$headers.Add("Authorization", "Bearer $token")

#Add the user to tenant & IaaS admins
for ($firstTenantNumber=1; $firstTenantNumber -le $numberOfTenants; $firstTenantNumber++)
{
 New-Variable -Name "var$firstTenantNumber" -Value $firstTenantNumber
 $tenantNumber = $firstTenantNumber.ToString("000")
 $tenantid = -join ($tenantIDPrefix,$tenantNumber)
$addTenantAdmins = Invoke-RestMethod -Method PUT -URI "https://$($VRA)/identity/api/authorization/tenants/$($tenantID)/principals/$($tenantAdmins)/roles/$($tenantRoleID)" -headers $headers -body "{}"

$addIaaSAdmins = Invoke-RestMethod -Method PUT -URI "https://$($VRA)/identity/api/authorization/tenants/$($tenantID)/principals/$($iaasAdmins)/roles/$($iaasRoleID)" -headers $headers -body "{}"
Write-Host "Domain groups added to as tenant & IaaS admins for tenant $($tenantid) " -ForegroundColor Green
}
 }

# Call All functions to setup tenants
getvRAAuthToken; createvRATenant; createvRALocalAdminUser; updatevRALocalAdminUserRoles; createvRATenantDirectory; addDomainGroupstovRAAdmins 

Distributed vRA validation script

From time to time a distributed vRA deployment can have issues…here is a quick script to verify and validate the important components are up and functioning…without the need to log into multiple components. Here is a diagram of my distributed vRA setup

Distributed vRA v4

And here is the script! It has multiple functions to do the following

  • Basic ping tests to each component
  • Get the status of all vRA component services
  • Test the Web & manager server URLs

# Script to check the status of each component of a distributed vRA deployment
# Modify the hostnames to match your environment
$vRAAppliance01FQDN = "vra01.domain.local"
$vRAAppliance02FQDN = "vra02.domain.local"
$vRAWeb01FQDN = "web01.domain.local"
$vRAWeb02FQDN = "web02.domain.local"
$vRAManager01FQDN = "manager01.domain.local"
$vRAManager02FQDN = "manager02.domain.local"
$vRADEM01FQDN = "demw01.domain.local"
$vRADEM02FQDN = "demw02.domain.local"
$vRAAgent01FQDN = "agent01.domain.local"
$vRAAgent02FQDN = "agent02.domain.local"
$vRAComponentServiceURL = "https://vra-vip.domain.local/component-registry/services/status/current"
$webVIPURL = "https://web-vip.domain.local/WAPI"
$managerVIPURL = "https://manager-vip.domain.local/VMPS2"



### DO NOT MODIFY ANYTHING BELOW THIS LINE ###



Function pingHosts {
@"
===============================================================================
Performing basic ping test to each defined component
===============================================================================
"@
$vms = @($vRAAppliance01FQDN, $vRAAppliance02FQDN, $vRAWeb01FQDN, $vRAWeb02FQDN, $vRAManager01FQDN, $vRAManager02FQDN, $vRADEM01FQDN, $vRADEM02FQDN, $vRAAgent01FQDN, $vRAAgent02FQDN)
$collection = $()
foreach ($vm in $vms)
{
 $status = @{ "ServerName" = $vm; "TimeStamp" = (Get-Date -f s) }
 if (Test-Connection $vm -Count 1 -ea 0 -Quiet)
 { 
 $status["Ping Results"] = "Up"
 } 
 else 
 { 
 $status["Ping Results"] = "Down" 
 }
 New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
 $collection += $serverStatus

}
 }
 
Function testvRAServices {
@"
===============================================================================
Getting Status of all vRA component services
===============================================================================
"@
Write-Host "Checking status of vRA Component Services" -ForegroundColor Yellow
# Request Service Information from $vRAComponentServiceURL
$vRAURL = Invoke-WebRequest $vRAComponentServiceURL
# Convert the Json response
$json = ConvertFrom-Json -InputObject $vRAURL.content
# Get Service name & status
$serviceInfo = $json.content
# Loop through each service
foreach ($service in $serviceInfo) {
# Get the Service Name
$serviceName = $service.serviceName
# Get the Service status
$serviceStatus = $service.serviceStatus.serviceInitializationStatus
# If Service Status is blank report it as BLANK POSSIBLY STOPPED
 if (!$serviceStatus) {
 $serviceStatus = "BLANK - POSSIBLY STOPPED?"
 }
# If Service Status is FAILED print to screen in red 
if ($serviceStatus -eq "FAILED") {
 write-host "$serviceName $serviceStatus" -ForeGroundColor Red
 }
# Otherwise print to screen as normal (Remove this if you only want to report failed services) 
 else {
 Write-Host "$serviceName $serviceStatus"}
 }

}


Function testWebVIP {
@"
===============================================================================
Checking status of vRA Web API URL
===============================================================================
"@
Write-Host "Testing IaaS Web Service VIP URL $webVIPURL" -ForegroundColor Yellow
# Create Web Request
$HTTP_Request = [System.Net.WebRequest]::Create($webVIPURL)

# Get a response
$HTTP_Response = $HTTP_Request.GetResponse()

# Get the HTTP code
$HTTP_Status = [int]$HTTP_Response.StatusCode

If ($HTTP_Status -eq 200) { 
 Write-Host "IaaS Web Service is OK!" -ForegroundColor Green
 # Close HTTP request
$HTTP_Response.Close()
}
Else {
 Write-Host "IaaS Web Service is not responding. Restart IIS on Web01. If that does not resolve then Reboot Web01" -ForegroundColor Red
}
 }
 
Function testMgrVIP {
@"
===============================================================================
Checking status of vRA Manager API URL
===============================================================================
"@
Write-Host "Testing IaaS Manager Service VIP URL $managerVIPURL" -ForegroundColor Yellow
# Create Web Request
$HTTP_Request = [System.Net.WebRequest]::Create($managerVIPURL)

# Get a response
$HTTP_Response = $HTTP_Request.GetResponse()

# Get the HTTP code
$HTTP_Status = [int]$HTTP_Response.StatusCode

If ($HTTP_Status -eq 200) { 
 Write-Host "IaaS Manager Service is OK!" -ForegroundColor Green
 # Close HTTP request
$HTTP_Response.Close()
}
Else {
 Write-Host "IaaS Manager Service is not responding. Ensure all vRA services are running on manager01. If that does not resolve then Reboot manager01" -ForegroundColor Red
}
 } 
 

 pingHosts; testvRAServices; testWebVIP; testMgrVIP

The function pingHosts is a basic ping test to each defined vRA component

The function testvRAServices was an interesting one to write as I’m not overly familiar with working with APIs so it was a learning experience. I wanted to be able to report the status of all vRA services listed on the VAMI administration UI of a vRA appliance (https://vra:5480). The URL that the services are listed on is https://vra-vip.domain.local/component-registry/services/status/current so using the powershell Invoke-WebRequest you get back the page information.

Invoke-WebRequest

Line 56 in the script puts the page contents into a variable we can work with. You can see that the information we want is stored in Content (ServiceStatus) in Json format so you need to take that Json and convert it to  to powershell readable text using the ConvertFrom-Json function (ConvertFrom-Json converts a JSON-formatted string to a custom object (PSCustomObject) that has a property for each field in the JSON string) Line 58 does this

We then put each service into the $serviceinfo variable and loop through them to get the service name and service status.

Side note here: Originally I was querying $json.content.serviceStatus to get the details i wanted but i noticed I wasnt getting a full list of services, i was getting some blank content and also some duplicate service names. This is how i was doing it

$vRAURL = Invoke-WebRequest "https://vra-vip.vlab.local/component-registry/services/status/current" 
$json = ConvertFrom-Json -InputObject $vRAURL.content 
$serviceInfo = $json.content.serviceStatus | Select serviceName,serviceInitializationStatus $serviceInfo 

Here is that that returns. As you can see its not the full list and there is a duplicate entry so its not much use

Duplicate Results

I dug a little into the API and it seems that it does indeed contain inconsistent information. Here is an excerpt with some issues where the actual service name is content-management but the serviceStatus reports the name as advanced-designer-service

Service Name issue

So to get an accurate list i queried the serviceName field to get the name and the serviceStatus.serviceInitializationStatus to get the service status. Unfortunately doing it this way doesnt allow creating a nice formatted table (at least i havent figured out how to do it yet!) but i did get it to print out each service & status on the same line.

Line 68: In my lab i use a vRO appliance so the internal vRO service on the vRA appliance is stopped. The service status comes back blank so i added a check to report blank service status as “BLANK – POSSIBLY STOPPED?”.

Line 72: I also added a check to print any failed services in red so they stand out.

The testWebVIP and testManagerVIP functions use the powershell System.Net.WebRequest to get the HTTP status code for a given URL. If the status code comes back as 200 then everything is ok. If not there is an issue with your IaaS components

So there you have it. A quick way to verify the status of all of the important vRealize Automation components and services. In my example below the iaas-service is in a failed state (The driving reason for creating this script! 🙂 )

Script Results