Invoke PowerShell Scripts using vRO 7.0 with a vRA 7.0 Self Service Portal Part-1.

Invoke PowerShell Scripts using vRO 7.0 with a vRA 7.0 Self Service Portal Part-1.

Invoke PowerShell Scripts using vRO, Part-1

In this blogpost i would like to share my experience of integrating vRealize Automation with vRealize Orchestrator and how seamless it is to create a custom workflow which can invoke powershell script from vRO powershell host.

In part one of this blog we would take deep dive into the crux of the solution which is vRO which allows you to create customized workflows

As we all know PowerShell is one of the most admin friendly and integrates with almost every product we have in market now.

With this integration we can create a custom self service portal using vRA and provide multitudes of service offerings which use powershell script’s and vRO powershell host in the background to perform an activity.

So lets get started with the details on the solution, Invoke PowerShell Scripts from VRO .

Prerequisites involve, a powershell host added to the vRO appliance, you should be able to see a green check-mark once the workflow successfully completes its execution. You can follow these excellent blog posts on how to add a powershell host if you have not done it yet.

Spas Kaloferov’s Blog – Adding vCO Powershell Host with account other than the default domain administrator account

DefinIT – Configuring a vRO/vCAC PowerShell host with Basic Authentication

test

Also if you are using an external orchestra-tor server make sure that you have configured it to be used with v Realize Automation.

test

Once all the pre-requisites are met, lets start with the integration script and workflows.

For my testing purpose i would be illustrating the integration by creating a workflow which invokes a powershell script which would basically uses the PowerCLI cmdlets to create a test VM based on the form input provided either through vra or vro.

Log into the vRO console, and change the view to design mode. and create a new workflow called as “Create_Test_VM”.

test

Next Click on the edit (pencil icon) workflow button present on the top end of the screen to edit the workflow.

Drag and drop the scrip-table task and Invoke an External script schema integration components into the design whiteboard in schema view.

test

Next lets start configuring each of these elements.

Click on the “Inputs” tab and define the parameters which would be fed into the powershell script.

test

Note that the password to connect to the vcenter can be declared a “Secure String” type which allows you to mask out the password with  “******” in the input form :), now that’s really cool without the use of any custom programming.

test

 

Once we define the input parameters, head over to the output tab, and create a new parameter called as “scriptarg” and move it as an attribute, this attribute will contain the format of how we pass the parameters to the powershell script.

test

Next move to the  schema view and edit the properties of the “Scriptable Task” schema object.

Click on the “Bind to workflow parameter button” and select the input parameters we defined in the previous step.

test

Once you select all the parameters, perform the similar step on the output tab and select the bind the output to the attribute “scriptarg”

test

Next click on the visual binding tab and make sure that you see a similar binding structure as depicted below.

test

Next move on to the scripting tab and define the exact arguments which would be passed on to the powershell script.

test

Note that you would need to take care of spaces between the parameters or else the powershell script would not be able to understand the parameter format.

Below script block represents the exact format of the parameters with spaces in b/w

scriptarg = "-vcenterserver" + " " + vcenterserver + " " + "-username" + " " + username + " " + "-password" + " " + password + " " + "-vmname" + " " + vmname + " " + "-vpgroup" + vpgroup;

Next edit the properties of the “Invoke an external script” schema object and select “scriptarg” as the argument to the powershell script.

test

Click on save and close the workflow.

Woaahh!, now that was a huge set of steps, but i hope that my screen-capture based step by step procedure helped you out a lot.

Next lets test the workflow , click on run, and feed in the required parameters and click on submit to Invoke PowerShell Scripts using VRO .

Note that you need to install PowerCLI  on the powershell host, also the script file during my test was placed at c:\users location in the powershell host

Invoke PowerShell Scripts using VRO

Now if you log into your vcenter server you would be greeted with your VM cloning task in action 🙂

test

Here’s the VM Cloning script which gets called.

param(
$vmname,
$vcenterserver,
$username,
$password,
$vpgroup
)


Add-PSSnapin VMware.VimAutomation.Core


$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$vccredential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)

Connect-VIServer $vcenterserver -Credential $vccredential | Out-Null


Get-OSCustomizationSpec -Type NonPersistent | Remove-OSCustomizationSpec -Confirm:$false
$Spec = Get-OSCustomizationSpec 'Win2k12R2OSSpec' |  New-OSCustomizationSpec -Name 'PRSpec' -Type NonPersistent
$Spec = Get-OSCustomizationNicMapping -Spec 'PRSpec' | Set-OSCustomizationNicMapping -IPmode UseStaticIP -IpAddress '<your parameters here>' -SubnetMask '<your parameters here>' -DefaultGateway '<your parameters here>' -Dns '<your parameters here>'
$osCust = Get-OSCustomizationSpec -Name 'PRSpec'
$clonetask = New-VM -template WIN2K12R2-TEMPLATE -vmhost '<your parameters here>' -name $vmname -runasync -Datastore '<your parameters here>' -OSCustomizationSpec $osCust
Get-Task -Id $clonetask.ID | Wait-Task

start-vm $vmname | wait-tools

Start-Sleep 5

# Connect and assign network name
Get-VM $VMname | Get-NetworkAdapter | Set-NetworkAdapter -Connected:$true -StartConnected:$true -vpgroup $vpgroup -Confirm:$false

##### Wait for the VM to be powered on #####

$VM = Get-VM -Name $vmname

While ($vm.ExtensionData.Runtime.PowerState -ne 'poweredOn')
{
    Start-Sleep -Seconds 1
    $vm.ExtensionData.UpdateViewData('Runtime.PowerState')
}


function WaitVM-Customization {
 
[CmdletBinding()] 
param( 
   # VMs to monitor for OS customization completion 
   [Parameter(Mandatory=$true)] 
   [ValidateNotNullOrEmpty()] 
   [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $vm, 
   
   # timeout in seconds to wait 
   [int] $timeoutSeconds = 600 
)

<#

.SYNOPSIS 
Waits customization process for list virtual machines to completes.

.DESCRIPTION 
Waits customization process for list virtual machines to completes. 
The script returns if customization process ends for all virtual machines or if the specified timeout elapses. 
The script returns PSObject for each specified VM. 
The output object has VM and CustomizationStatus properties.

.EXAMPLE 
$vm = 1..10 | foreach { New-VM -Template WindowsXPTemplate -OSCustomizationSpec WindowsXPCustomizaionSpec -Name "winxp-$_" } 
.\WaitVmCustomization.ps1 -vmList $vm -timeoutSeconds 600

.NOTES 
The script is based on sveral vCenter events. 
* VmStarting event - this event is posted on power on operation 
* CustomizationStartedEvent event - this event is posted for VM when customiztion has started 
* CustomizationSucceeded event - this event is posted for VM when customization has successfully completed 
* CustomizationFailed - this event is posted for VM when customization has failed

Possible CustomizationStatus values are: 
* "VmNotStarted" - if it was not found VmStarting event for specific VM. 
* "CustomizationNotStarted" - if it was not found CustomizationStarterdEvent for specific VM. 
* "CustomizationStarted" - CustomizationStartedEvent was found, but Succeeded or Failed event were not found 
* "CustomizationSucceeded" - CustomizationSucceeded event was found for this VM 
* "CustomizationFailed" - CustomizationFailed event wass found for this VM

#>



# constants for status 
      $STATUS_VM_NOT_STARTED = "VmNotStarted" 
      $STATUS_CUSTOMIZATION_NOT_STARTED = "CustomizationNotStarted" 
      $STATUS_STARTED = "CustomizationStarted" 
      $STATUS_SUCCEEDED = "CustomizationSucceeded" 
      $STATUS_FAILED = "CustomizationFailed" 
      
      $STATUS_NOT_COMPLETED_LIST = @( $STATUS_CUSTOMIZATION_NOT_STARTED, $STATUS_STARTED ) 
      
# constants for event types      
      $EVENT_TYPE_CUSTOMIZATION_STARTED = "VMware.Vim.CustomizationStartedEvent" 
      $EVENT_TYPE_CUSTOMIZATION_SUCCEEDED = "VMware.Vim.CustomizationSucceeded" 
      $EVENT_TYPE_CUSTOMIZATION_FAILED = "VMware.Vim.CustomizationFailed" 
      $EVENT_TYPE_VM_START = "VMware.Vim.VmStartingEvent"

# seconds to sleep before next loop iteration 
      $WAIT_INTERVAL_SECONDS = 15 
      
function main($vm, $timeoutSeconds) { 
   # the moment in which the script has started 
   # the maximum time to wait is measured from this moment 
   $startTime = Get-Date 
   
   # we will check for "start vm" events 5 minutes before current moment 
   $startTimeEventFilter = $startTime.AddMinutes(-5) 
   
   # initializing list of helper objects 
   # each object holds VM, customization status and the last VmStarting event 
   $vmDescriptors = New-Object System.Collections.ArrayList 
   foreach($vm in $vm) { 
      Write-Host "Start monitoring customization process for vm '$vm'" 
      $obj = "" | select VM,CustomizationStatus,StartVMEvent 
      $obj.VM = $vm 
      # getting all events for the $vm, 
      #  filter them by type, 
      #  sort them by CreatedTime, 
      #  get the last one 
      $obj.StartVMEvent = Get-VIEvent -Entity $vm -Start $startTimeEventFilter | ` 
         where { $_ -is $EVENT_TYPE_VM_START } | 
         Sort CreatedTime | 
         Select -Last 1 
         
      if (-not $obj.StartVMEvent) { 
         $obj.CustomizationStatus = $STATUS_VM_NOT_STARTED 
      } else { 
         $obj.CustomizationStatus = $STATUS_CUSTOMIZATION_NOT_STARTED 
      } 
      
      [void]($vmDescriptors.Add($obj)) 
   }         
   
   # declaring script block which will evaulate whether 
   # to continue waiting for customization status update 
   $shouldContinue = { 
      # is there more virtual machines to wait for customization status update 
      # we should wait for VMs with status $STATUS_STARTED or $STATUS_CUSTOMIZATION_NOT_STARTED 
      $notCompletedVms = $vmDescriptors | ` 
         where { $STATUS_NOT_COMPLETED_LIST -contains $_.CustomizationStatus }

      # evaulating the time that has elapsed since the script is running 
      $currentTime = Get-Date 
      $timeElapsed = $currentTime - $startTime 
      
      $timoutNotElapsed = ($timeElapsed.TotalSeconds -lt $timeoutSeconds) 
      
      # returns $true if there are more virtual machines to monitor 
      # and the timeout is not elapsed 
      return ( ($notCompletedVms -ne $null) -and ($timoutNotElapsed) ) 
   } 
      
   while (& $shouldContinue) { 
      foreach ($vmItem in $vmDescriptors) { 
         $vmName = $vmItem.VM.Name 
         switch ($vmItem.CustomizationStatus) { 
            $STATUS_CUSTOMIZATION_NOT_STARTED { 
               # we should check for customization started event 
               $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime 
               $startEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_STARTED } 
               if ($startEvent) { 
                  $vmItem.CustomizationStatus = $STATUS_STARTED 
                  Write-Host "VI Event Generated - Customization for VM '$vmName' has started" -ForegroundColor Yellow -BackgroundColor Black
               } 
               break; 
            } 
            $STATUS_STARTED { 
               # we should check for customization succeeded or failed event 
               $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime 
               $succeedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_SUCCEEDED } 
               $failedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_FAILED } 
               if ($succeedEvent) { 
                  $vmItem.CustomizationStatus = $STATUS_SUCCEEDED 
                  Write-Host "VI Event Generated - Customization for VM '$vmName' has successfully completed" -ForegroundColor Green -BackgroundColor Black
               } 
               if ($failedEvent) { 
                  $vmItem.CustomizationStatus = $STATUS_FAILED 
                  Write-Host "Customization for VM '$vmName' has failed" 
               } 
               break; 
            } 
            default { 
               # in all other cases there is nothing to do 
               #    $STATUS_VM_NOT_STARTED -> if VM is not started, there's no point to look for customization events 
               #    $STATUS_SUCCEEDED -> customization is already succeeded 
               #    $STATUS_FAILED -> customization 
               break; 
            } 
         } # enf of switch 
      } # end of the freach loop 
      
      Write-Host "Awaiting OS Customization VI Event, Sleeping for $WAIT_INTERVAL_SECONDS seconds" -BackgroundColor Black
            
      Sleep $WAIT_INTERVAL_SECONDS 
   } # end of while loop 
   
   # preparing result, without the helper column StartVMEvent 
   $result = $vmDescriptors | select VM,CustomizationStatus 
   return $result 
}

main $vm $timeoutSeconds

}

WaitVM-Customization -vm $vm

I hope you enjoyed this blogpost on “Invoke PowerShell Scripts using VRO” and found the information useful, there is no limit to the endless possibilities we can attain with powershell an powercli in hand !!

Stay tuned for my next post where i would show how easy it is to integrate this solution with vRealize automation and offer this service to end users via a self service portal.

Do Check out my other posts on VRO, VRA and PowerShell

Share this post

11 thoughts on “Invoke PowerShell Scripts using vRO 7.0 with a vRA 7.0 Self Service Portal Part-1.

  1. thanks, great post, got it all set up! The only thing that’s off is that password isn’t passed to the script. Any idea why this is failing?

    Reply
  2. This is great. IS there a way I can add the following 3 items in to this script:

    1. Rename the windows machine
    2. Set the IP, Subnet mask and default gateway (From user input)
    3. Join it to the domain

    Reply

Post Comment