One of my current tasks is to leverage vRealize Automation Orchestrator to meet the following use case.
Get the next available subnet from InfoBlox
Reserve the gateway and other IP’s in the new subnet
Create a new NSX-T segment
Create new NSX-T security groups
Discover the new segment in vRealize Automation Cloud
Assign the new InfoBlox to the discovered Fabric Network
Create a new Network Profile in vRA Cloud
This weeks goal was to get the InfoBlox part working. Well I had it working two years ago, but couldn’t remember how I did it (CRS).
Today I’ll discuss how to use vRO to get the next available subnet from InfoBlox. The solution uses a PSM I build, along with the PowerShell script which actually does the heavy lifting. One key difference between my solution and the one from VMware’s documentation is the naming of the zip file. This affects how to import and use it in vRO.
The code used in this example is available in this GitHub repo. Clone the repo, then run the following command to zip up the files.
zip -r -x ".git/*" -x "README.md" -X nextibsubnet.zip .
Next import the zip file into vRO, add some inputs, modify the output, then finally run it.
Within vRO, add a new Action. Then change the script type to “PowerCli 12 (PowerShell 7.1).
PowerShell Script Type
Change the Type to ‘Zip’ by clicking on the dropdown under ‘Type’ and selecting ‘Zip’
Click ‘Import’, then browse to the folder containing the zip file from earlier in the article.
You will notice the name is not ‘nextibsubnet.zip’ but InfobloxGetNextAvailableSubnet.zip. The imported zip assumes the name of the vRO Action.
Now the biggest difference between my approach and the VMware way. If you look at the cloned folder you will see a file named ‘getNextAvailableIbSubnet.ps1’. The VMware document called this file ‘handler.ps1’. Instead of putting in ‘handler.handler’ in ‘Entry Handler’, I’ll use ‘getNextAvailableIbSubnet.handler. This tells vRO to look inside ‘getNextAvailableIbSubnet.ps1’ for a function called ‘handler’.
Next we need to change the return type to Properties, and add a few inputs.
Save and run. And if everything is in order, you should get the next InfoBlox subnet from 10.10.0.0/24. The results from the action run.
My current customer asked if they could use the same vSphere template as an AWS AMI. The current vSphere template has a custom disk layout to help them troubleshoot issues. The default single disk layout for AMI’s actually hinders their troubleshooting methodology.
Aside from the custom disk layout, I know VMtools would have to be replaced with cloud-init. Sure no problem. RIGHT! Well actually it wasn’t that hard.
Well I was finally able get it to work, and learned a bunch along the way. Those lessons include,
The RHEL default DHCP client is incompatible with AWS.
EFI bios is only supported in larger, more expensive instances.
AWS VM image import.
Make sure to enable ‘disable_vmware_customization’, if that made sense.
Requirements
AWS roles, policies and permissions per this document.
S3 bucket (packer-import-example) to store the VMDK until it is imported.
Basic IAM user (packer) with the correct permissions assigned (see above).
vSphere environment to build the image.
A RHEL 8.x DVD ISO for installation.
HTTP repo to store the kickstart file.
Now down to brass tacks. To be honest it took lots of trial and error (mostly error) to get this working right. For example, on one pass Cloud-Init wouldn’t run on the imported AMI. After looking at cloud.cfg I noticed ‘disable_vmware_customization’ was set to false instead of ‘true’. Another error occurred when my first import attempt failed as the machine did not have a ‘DHCP client’. That was odd as it booted up fine in vSphere and got an IP Address. Apparently AWS only supports certain DHCP clients. Go figure.
Eventually the machine booted properly in AWS, with the user-data applied correctly. The working user-data is in the repo’s cloud-init directory.
And my super simple vRAC blue print even worked. This simple BP adds a new user, assigns a password, and grants it SUDO permissions.
Successful vRA Cloud Deployment
A couple of notes on the packer amazon-import post processor. Those include,
The images are encrypted by default, even tho the default for ‘ami_encrypt’ is false by default.
‘ami_name’ requires the AWS permission of ec2:CopyImage on the policy for the import role.
Don’t use the default encryption key if you wish to share this. You’ll need a Customer Managed Key (CMK). The import role (vmimport) will need to be a key user. You can set this with ‘ami_kms_key’ set to the Id of the CMK (i.e., ebea!!!!!!!!-aaaa-zzzz-xxxxxxxxxxxxxx)
The CMK needs to be shared with the target customer before sharing the AMI. ‘ami_org_arns’ allows you to set the organizations you’d like to share the AMI with.
In this second part, I’ll discuss the actual Code Stream pipeline.
As stated before, the inspiration was William Lams wonderful Power Shell scripts to deploy a nested environment from a CLI. His original logic was retained as much as possible, however due to the nature of K8S a few things had to be changed. I’ll try to address those as they come up.
After some thought I decided to NOT allow the requester to select the amount of Memory, vCPU, or VSAN size. Each Esxi host has 24G of Ram, 4 vCPU, and contributes a touch over 100G to the VSAN. The resulting cluster has 72G of RAM, 12 vCPUs and a roughly 300G VSAN. Only Standard vSwitches are configured in each host.
The code, pipeline and other information is available on this github repo.
Deployment of the Esxi hosts is initiated by ‘deployNestedEsxi.ps1’. There are few changes from the original script.
The OVA configuration is only grabbed once. Then only the specific host settings (IP Address and Name are changed.
The hosts are moved into a vApp once built.
The NetworkAdapter settings are performed after deployment.
Persisted the log to /var/workspace_cache/logs/vsphere-deployment-$BUILDTIME.log.
Deployment of the vCSA is handled by ‘deployVcsa.ps1’ Some notable changes from the original code include.
Hardcoded the SSO username to administrator@vsphere.local.
Hardcoded the size to ‘tiny’.
Save the log file to /var/workspace_cache/logs/NestedVcsa-$BUILDTIME.log.
Save the configuration template to /var/workspace_cache/vcsajson/NestedVcsa-$BUILDTIME.json.
Move the VCSA into the vApp after deployment is complete.
And finally ‘configureVc.ps1’ sets up the Cluster and VSAN. Some changed include.
Hardcoded the Datacenter name (DC), and Cluster (CL1).
Import the Esxi hosts by IP (No DNS records setup for the hosts or vCenter).
Append the configuration results to /var/workspace_cache/logs/vsphere-deployment-$BUILDTIME.log.
So there you go, down and simple Code Stream pipeline to deploy a nested vSphere environment in about an hour.
Stay tuned. The next article will include an NSX-T deployment.
One of my peers came up with an interesting use case today. His customer wanted to mount an existing disk on a virtual machine using a vRA Cloud day 2 action.
I couldn’t find an out of the box workflow or action on my vRO, which meant I had to do this thing from scratch.
After a quick look around I found a PowerCli cmdlet (New-Hardisk) which allowed me to mount an existing disk.
My initial attempts to just run it as a scriptable task resulted in the following error.
Hmm, so how do you increase the memory in a scriptable task? Simple, you can’t. Thus I had to move the script into an action, which does allow me to increase the memory. After some tinkering I found that 256M was sufficient to run the code.
function Handler($context, $inputs) {
# $inputs:
## vmName: string
## vcName: string (in configuration element)
## vcUsername: string (in configuration element)
## vcPassword: secureString (in configuration element)
## diskPath: string. Example in code.
# output:
## actionResult: Not used
$inputsString = $inputs | ConvertTo-Json -Compress
Write-Host "Inputs were $inputsString"
$output=@{status = 'done'}
# connect to viserver
Set-PowerCLIConfiguration -InvalidCertificateAction:Ignore -Confirm:$false
Connect-VIServer -Server $inputs.vcName -Protocol https -User $inputs.vcUsername -Password $inputs.vcPassword
# Get vm by name
Write-Host "vmName is $inputs.vmName"
$vm = Get-VM -Name $inputs.vmName
# New-HardDisk -VM $vm -DiskPath "[storage1] OtherVM/OtherVM.vmdk"
$result = New-HardDisk -VM $vm -DiskPath $inputs.diskPath
Write-Host "Result is $result"
return "It worked!"
}
Looking at the code, you will notice an input of vmName (used by PS to find the VM). Getting the vmName is actually pretty stupid simple using JavaScript. My first task in the WF takes care of this.
// get the vmName
// $inputs.vm
// output: vmName
vmName = vm.name
The next step was to setup a resource action. The settings are shown in the following snapshot. Please note the setting within the green box. ‘vm’ is set with a binding action.
Changing the binding is fairly simple. Just click the binding link, then change the value to ‘with binding action’. The default values work just fine.
The disk I used in the test was actually a copy of another VM boot disk. It was copied over to another datastore, then renamed to ‘ExistingDisk2.vmdk’. The full diskPath was [dag-nfs] ExistingDisk/ExistingDisk2.vmdk.
Running the day 2 action on deployed machine seemed to work, as the WF logs show.
So there you have a basic PolyGlot vRO workflow using PowerCli and JavaScript.
I trust this quick blog was helpful in some small way.
My current customer needs to use 172.18.0.0/16 for their new VMWare Cloud on AWS cluster. However we tried this in the past and were getting a “NO ROUTE TO HOST” error when trying to add the VMC vCenter as a cloud account.
The problem was eventually traced back to the ‘on-prem-collector’ (br-57b69aa2bd0f) network in the Cloud Proxy which also uses the same subnet.
Let’s say the vCenters IP is 172.18.32.10. From inside cloudassembly-sddc-agent container, I try to connect to the vCenter. Eventually getting a ‘No route to host’ error. Can anyone say classic overlapping IP space?
We reached out to our VMWare Customer Success Team and TAM, who eventually provided a way to change the Cloud Proxy docker and on-prem-collector subnets.
Now for the obligatory warning. Don’t try this in production without having GSS sign off on it.
In this example I’m going to change the docker network to 192.168.0.0/24 and the on-prem-collector network to 192.168.1.0/24.
First to update the docker interface range.
Add the following two lines to /etc/docker/daemon.json. Don’t forget to add the necessary comma(s). Then save and close.
Check to see which containers are using this network with docker network inspect on-prem-collector. Mine had two, cloudassembly-sddc-agent, cloudassembly-cmx-agent.
In this blog I’ll explore the new Secret capability in vRealize Automation Cloud. The use case includes the following:
Deploy a CentOS 8 machine
Store the new user password and SSH key in a Secret
Configure the machine using Cloud-Init
Assign the password and SSH key from a vRA Cloud Secret
Verify the password and SSH key assignment.
First, add two Secrets. Go to Infrastructure -> Secrets, then click NEW SECRET.
The first one will be the SSH key. Find your project, give it a name, then paste in the key. Click CREATE to save the values. Repeat the process for the Password secret.
The Cloud Template is fairly straight forward. The new user password will be assigned the secret.Blog_Password value, and the ssh_authorized_keys comes from secret.Blog_SSH_Key value.
Now a look at how the secret values are displayed on a deployed machine. Open the deployment, then click on the machine. Expand Cloud Config to view the secret values sent to the machine.
As you can see the values for the new user are encrypted, and do not match stored secret values (The user password is set to VMware1!). Good so far.
Now to see if the password and SSH key actually work. A quick SSH using the key should be sufficient.
Oops. Looks like the key didn’t work, but I was able to login using the password. Time for a bit of troubleshooting. Using elevated permissions (set in Cloud-Init), I take a look at the cloud-init config sent down to the machine.
#more /var/lib/cloud/instance/cloud-config.txt
Hmm, looks the key had a line return in it.
I’ll need to edit/update the Blog_SSH_Key secret. After finding my troublesome secret, I click Edit.
The previously stored value is not viewable, I can only update it.
The new value is viewable until I save it. I made sure this one didn’t have a line return in it. The changes are committed when I click Save.
Now to test the change on a newly deployed machine. I’ll use the same SSH command, with the exception of changing the IP address.
Success! I was able to log in using the key.
In this blog I explored a simple application using two vRA Cloud Secrets, troubleshooting, and updating a secret Value. The VMware developers did a great job. I’m sure the new feature will prove to be very valuable.
I’m not sure when this will get pushed down into vRA 8.x. Please contact your VMware team for more information.
The program “is about giving back to the community beyond your day job”.
One way I give back is by posting new and unique content here once or twice a month. Sometimes a post is simply me clearing a thought before the weekend, completing a commitment to a BU, or documenting something before moving on to another task. It doesn’t take long, but could open the door for one of my peers.
My most frequently used benefit is the vExpert and Cloud Management Slack channels. I normally learn something new every-week. And it sure does feel good to help a peer struggling with something I’ve already tinkered with.
Here’s a list of some of the benefits for receiving the award.
Networking with 2,000+ vExperts / Information Sharing
Knowledge Expansion on VMware & Partner Technology
Opportunity to apply for vExpert BU Lead Subprograms
Possible Job Opportunities
Direct Access to VMware Business Units via Subprograms
Blog Traffic Boost through Advocacy, @vExpert, @VMware, VMware Launch & Announcement Campaigns
1 Year VMware Licenses for Home Labs for almost all Products & Some Partner Products
Private VMware & VMware Partner Sessions
Gifts from VMware and VMware Partners
vExpert Celebration Parties at both VMworld US and VMworld Europe with VMware CEO, Pat Gelsinger
VMware Advocacy Platform Invite (share your content to thousands of vExperts & VMware employees who amplify your content via their social channels)
Private Slack Channels for vExpert and the BU Lead Subprograms
The applications close on January 9th, 2021. Start working on those applications now.
First off, I found the built in Code Stream REST tasks do not have a retry. I learned this the hard way when they had issues with their cloud offering last month. At times it would get a 500 error back when making a request, resulting in a failed execution.
This forced me to look at Python custom integrations which would retry until the correct success code was returned. I was able to get the pipeline working, but it did have a lot of repetitive code, lacked the ability to limit the number of retries, and was based on Python 2.
Seeing the error of my ways, I decided to again refactor the code with a custom module (For the repetitive code), and migrate to Python 3.
The original docker image was CentOS based and did not have Python 3 installed. Instead of just installing Python 3 thus increasing the size of the image, I opted to start with the Docker Official Python 3 image. I’ll get to the build file later.
Now on to the actual refactoring. Here I wanted to combine the reused code into a custom python module. My REST calls include POST (To get a Bearer Token), GET (With and without a Filter), PATCH (To update Image Mappings), and DELETE (To delete the test Image Profile and Cloud Template).
This module snippet includes PostBearerToken_session which returns the bearToken and other headers. GetFilteredVrac_sessions returns a filtered GET request. It also limits the retries to 5 attempts.
import requests
import logging
import os
import json
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
# retry strategy to tolerate API errors. Will retry if the status_forcelist matches.
retry_strategy = Retry (
total=5,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["GET", "POST"],
backoff_factor=2,
raise_on_status=True,
)
adapter = HTTPAdapter(max_retries=retry_strategy)
https = requests.Session()
https.mount("https://", adapter)
vRacUrl = "https://api.mgmt.cloud.vmware.com"
def PostBearerToken_session(refreshToken):
# Post to get the bearerToken from refreshToken
# Will return the headers with Authorization populated
# Build post payload
pl = {}
pl['refreshToken'] = refreshToken.replace('\n', '')
logging.info('payload is ' + json.dumps(pl))
loginURL = vRacUrl + "/iaas/api/login"
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
r = https.post(loginURL, json=pl, headers=headers)
responseJson = r.json()
token = "Bearer " + responseJson["token"]
headers['Authorization']=token
return headers
def GetFilteredVrac_sessions(requestUrl, headers, requestFilter):
# Get a thing using a filter
requestUrl = vRacUrl + requestUrl + requestFilter
print(requestUrl)
adapter = HTTPAdapter(max_retries=retry_strategy)
https = requests.Session()
https.mount("https://", adapter)
r = https.get(requestUrl, headers=headers)
responseJson = r.json()
return responseJson
Here is the working Code Stream Custom Integration used to test this module. It will get the headers, then send a filtered request using the Cloud Account Name. It then pulls some information out of the Payload and prints it out. (Sorry for formatting).
runtime: "python3"
code: |
import json
import requests
import os
import sys
import logging
# append /build to the path
# this is where the custom python module is copied to
sys.path.append('/build')
import vRAC
# context.py is automatically added.
from context import getInput, setOutput
def main():
def main():
refreshToken=getInput('RefreshToken')
# now with new model
# authHeaders will have all of the required headers, including the bearerToken
authHeaders=vRAC.PostBearerToken_session(refreshToken)
# test the getFunction with filter
requestUrl = "/iaas/api/cloud-accounts"
requestFilter = "?$filter=name eq '" + getInput('cloudAccountName') + "'"
# get the cloudAccount by name
cloudAccountJson=vRAC.GetFilteredVrac_sessions(requestUrl, authHeaders, requestFilter)
# get some specific data out for later
cloudAccountId = cloudAccountJson['content'][0]['id']
logging.info('cloudAccountId: ' + cloudAccountId)
if name == 'main':
main()
inputProperties: # Enter fields for input section of a task
# Password input
- name: RefreshToken
type: text
title: vRAC Refresh Token
placeHolder: 'secret/password field'
defaultValue: changeMe
required: true
bindable: true
labelMessage: vRAC RefreshToken
- name: cloudAccountName
type: text
title: Cloud Account Name
placeHolder: vc.corp.local
defaultValue: vc.corp.local
required: true
bindable: true
labelMessage: Cloud Account Name
Next the new Docker image. This uses the official Python 3 image as a starting point. The build file copies everything over (Including the custom module and requirements.txt), then installs ‘requests’.
FROM python:3
WORKDIR /build
COPY . ./
RUN pip install --no-cache-dir -r requirements.txt
Now that the frame work is ready, it’s time to create the pipeline and test it. This is well documented here Creating and using pipelines in VMware Code Stream. Update the Host field with your predefined Docker Host, set the Builder image URL (Docker Hub repo and tag), and set the Working directory to ‘/build’ (to match WORKDIR in the Dockerfile).
Running the pipeline worked and returned the requested information.
This was a fairly brief article. I really just wanted to get everything written down before the weekend. I’ll have more in the near future.
First off, the Image Mappings shown in the UI cannot be updated via the API directly. The API allows you create, change and delete Image Profiles. An Image Profile contains the Image Mappings and is tied to a Region.
For example, in this image the IaC Image Mappings are displayed.
Here, some of same Image Mappings as seen when you GET the Image Profile by id.
{ "imageMapping": { "mapping": { "IaC-build-test-patch": { "id": "8fc331632163f53fd0c66e0407495504295b4c1c", "name": "", "description": "Template: vSphere-CentOS8-CUSTOM-2020.09.25.181019" }, "IaC-prod-profile": { "id": "2e2d31be93c59531d2c1eeeadc58f68b66174559", "name": "", "description": "Template: vSphere-CentOS8-CUSTOM-2020.09.25.144314" }, "IaC-test-profile": { "id": "842c91f05185978d62d201df3b47d1505cf3fea3", "name": "", "description": "Generic CentOS 7 template with cloud-init installed and VM hardware version 13 (compatible with ESXi 6.5 or greater)." } } }, "regionId": "71cecc477594a67558b9d5xxxxxxx", "name": "IaC-build-test-profile", "description": "Packer build image for testing" }
But how do you get the Image Profile Id? I ended up using a filter based on the externalRegionId (I used another filtered search to find the externalRegionId by Region Name).
Then using cloudAccountJson returned previously, I built a new body using the following (partial) code (I couldn’t get the formatting right, hence the image.)
Now some gotcha’s.
First, remember that the image mappings are tied to the region. You will loose any Image Mappings NOT included in the POST/PATCH Body. Make sure you back up the Image Profile settings (Do a get by Image Profile Id) before attempting to change the mappings via the API.
Secondly, an Image Profile does not have a name by default. You need to set this via the API. Why would you need it? Well you may want to find the Image Profile by name later. My current customer creates new customer Image Profiles via the API and uses a ‘tag’ like naming convention.
Thirdly, I’ve experienced several 500 errors when interfacing with the vRA Cloud API. The out of box Code Stream REST tasks do not retry. I ended up writing python custom integrations as a work around. These retry until receiving the correct response code (I’ve seen up to 15 500 errors before getting a 200).
This is just one thing I’ve learned about the vRA Cloud API, and Code Stream. I’ll post more as I have time.
I’ve been anxiously waiting for the new Terraform Resources for vRealize Automation Cloud. Well the wait is finally over. Version 8.20 was released on 8/30/2020. You can view the Release Notes here.
Now why would you want to use Terraform in vRA Cloud? You can already do a bunch out of the box. But what if you wanted to deploy an AWS EC2 instance with an encrypted boot disk? That is not available.
Terraform to the rescue. You can use Terraform to fill those kind of gaps without using a vRealize Orchestrator or Extensibility Appliance.
In this article I’ll show you how to deploy a basic AWS EC2 instance with an encrypted boot disk using the new Terraform Resource.
The Terraform configuration files and blueprint are available here.
Now on to the good stuff. Within vRAC create a new Cloud Template (renamed from Blueprints), by clicking on Design -> NEW FROM -> Terraform.
Enter the Template name and project on the next page, then click Next. The next page is where you select your GitHub Repository, Commit and the Source Directory. Then click Next.
For this example I’m leaving all of the variables as they come from variables.tf in the source directory. Click Next. This will bring you to the designer page.
The code will look something like this, with one notable exception depending on how your repo’s directory structure is laid out. The wizard assumes your sourceDirectory is directly off the root. For example root/sourceDirectory. But what if you have a path that looks like root/terraform/sourceDirectory? The wizard will not add ‘/terraform’ automagically. You will need to fix it (sample is already fixed).
inputs:
region:
type: string
default: us-east-2
ssh_key_name:
type: string
default: changeMe
hostname:
type: string
default: changeMe
resources:
terraform:
type: Cloud.Terraform.Configuration
properties:
variables:
region: '${input.region}'
ssh_key_name: '${input.ssh_key_name}'
hostname: '${input.hostname}'
providers:
- name: aws
# List of available cloud zones: Will get populated during create from
cloudZone: *********
terraformVersion: 0.12.26
configurationSource:
repositoryId: XXXXXXXXX
commitId: XXXXXXXXX
sourceDirectory: /terraform/Basic AWS
If all goes as expected, you can now deploy the new template.
Make sure to change the default variable values to match your environment. The Ssh_key_name needs to exist in the region you are deploying into.
Click on the Deployment History to see the Terraform Plan and Apply logs. They can be viewed by clicking ‘Show Logs” on the PLAN_IN_PROGRESS or CREATE_IN_PROGRESS status lines.
The logs can also be viewed in a new browser tab by clicking on ‘View as plain text’ from the expanded ‘Show Logs’ window.
Hopefully the instance deployed correctly. If so, take a coffee break. This gives vRAC time to discover the new ‘aws_instance’ and enable some day 2 actions.
The day 2 actions vary depending on the deployed machine type. You can find more information on page 416 of the Using and Managing VMware Cloud Assembly documentation.
But did the boot disk actually get encrypted? Yes! Here is a screenshot of the boot volume.
As you can see, the new Terraform Resource is a great way to fill in vRAC deployment gaps without using extensibility.