Replacing VCF Certificates with PowerShell and the SDDC Manager API

While certificate management for VCF components controlled by the SDDC Manager can be achieved via the SDDC Manager UI, what we really want to do is automate the process as part of a larger standardised process to be run immediately post management bringup. This post will walk you through utilising PowerShell, PowerCLI and PowerVCF toolkits to fully automate the replacement of certificates on SDDc Manager, vCenter and the NSX-T Manager. A complete copy of this script as well as many others can be found at the github repo below.

Before you start, you will want to ensure that you have downloaded the latest PowerCLI module. Instructions can be found here. Once that is complete, you will then want to install the PowerVCF module. Instructions can be found here.

At the top of every script I add some code to create a logging function which allows me to create and display a log file as my script executes. I can populate the logfile with additional information using a -logonly switch if required. It also create directories to store json configuration files which are created on the fly in the script and which are needed to configure the certificates. The Start-process cmdlet opens a separate window when the script is executed to display the log file during script execution.

# Variables for the execution log file and json file directories.
$scriptDir = Split-Path $MyInvocation.MyCommand.Path # Log file directories will be created in the folder you execute the script from
$logPathDir = New-Item -ItemType Directory -Path "$scriptDir\Logs" -Force
$jsonPathDir = New-Item -ItemType Directory -Path "$scriptDir\json" -Force
$logfile = "$logPathDir\VVS-Log-_$(get-date -format `"yyyymmdd_hhmmss`").txt"

# Custom function to create a separate logging window for script execution.
Function logger($strMessage, [switch]$logOnly,[switch]$consoleOnly)
{
	$curDateTime = get-date -format "hh:mm:ss"
	$entry = "$curDateTime :> $strMessage"
    if ($consoleOnly) {
		write-host $entry
    } elseif ($logOnly) {
		$entry | out-file -Filepath $logfile -append
	} else {
        write-host $entry
		$entry | out-file -Filepath $logfile -append
	}
}

Logger "VCF Certificate Configuration (SDDC Manager, vCenter and NSX-T Manager"

Start-Process powershell -Argumentlist "`$host.UI.RawUI.WindowTitle = 'VLC Logging window';Get-Content '$logfile' -wait"

You’ll first want to define the variables that will be used by the various cmdlets within the script. Best practice dictates that you define your variables at the top of the script whenever possible, however, I have defined the required variables within different sections of the script to make it a bit easier for those less familiar with scripting to understand what information they are providing in context.

# SDDC Manager variables
$sddcManagerfqdn = "cmi-vcf01.elasticsky.org"
$ssoUser = "administrator@vsphere.local"
$ssoPass = "VMware123!"

Once they are defined, you can request a secure token for accessing the SDDC Manager API. Note: I am using a Start-Sleep periodically in my script as when running in a lab environment where resources are at a premium, it may take SDDC Manager a few extra seconds to catch up with itself.

# Authenticate to SDDC Manager using global variables defined at the top of the script
logger "Requesting secure token for SDDC Manager"
Request-VCFToken -fqdn $sddcManagerfqdn -username $ssoUser -password $ssoPass

Start-Sleep 5

After you have acquired a token, the first task is to register the Microsoft CA with SDDC Manager. If the customer does not yet have a CA configured, instruction for how to install, configure and create a VMware certificate template can be found here.

# Variables for adding the CA to SDDC Manager
$mscaUrl = "https://es-dc-01.elasticsky.org/certsrv"
$mscaUser = "svc-vcf-ca@elasticsky.org"
$mscaPassword = "VMware123!"

# Register Microsoft CA with SDDC Manager
logger "Register Microsoft CA with SDDC Manager"
Set-VCFMicrosoftCA -serverUrl $mscaUrl -username $mscaUser -password $mscaPassword -templateName VMware

Start-Sleep 5

The next block of variables are defined by executing a PowerVCF cmdlets (Get-VCFWorkloadDomain and Get-VCFManager) and adding qualifiers and filters to store system object data about the SDDC Manager, vCenter and NSX-T Manager. I then use the Add-Member cmdlet to add a crucial piece of configuration. The object data is used later when creating the CSR request. The SDDC Manager API does not have the object type as part of its standard definition, but is a required setting for creating and deploying certificates. The second block provides the standard settings requested by a CA when creating certificates. Note: email address is optional.

# Create Certificate Variables
logger "Create Certificate Variables"

$domainName = Get-VCFWorkloadDomain | Where-Object { $_.type -match "MANAGEMENT" } | Select-Object -ExpandProperty name
$vcenter = Get-VCFWorkloadDomain | Where-Object { $_.type -match "MANAGEMENT" } | Select-Object -ExpandProperty vcenters
$vcenter | Add-Member -Type NoteProperty -Name Type -Value "VCENTER"
$nsxTCluster = Get-VCFWorkloadDomain | Where-Object { $_.type -match "MANAGEMENT" } | Select-Object -ExpandProperty nsxtCluster
$nsxTCluster | Add-Member -MemberType NoteProperty -Name Type -Value "NSXT_MANAGER"
$sddcCertManager = Get-VCFManager | Select-Object id, fqdn
$sddcCertManager | Add-Member -MemberType NoteProperty -Name Type -Value "SDDC_MANAGER"

$country = "us"
$keySize = "2048"
$keyAlg = "RSA"
$locality = "Champaign"
$org = "ElasticSky"
$orgUnit = "IT"
$state = "IL"
$email = "administrator@elasticsky.org"

We are now ready to start the process of requesting CSRs, creating certificates and then installing them. The first part is creating the CSRs. The Request-VCFCertificateCSR cmdlet requires a JSON formatted configuration file containing the settings for the certificates. Here I am using a standard PowerShell cmdlets (New-Object and Add-Member) to create the content of the file and then I am converting it to JSON using the ConvertTo-Json cmdlet. In this example I am using what would now be considered a legacy format to create the object. There are shorthand ways of creating objects, but for those less familiar with scripting, I am spelling it out. Note also that I am piping the results of each cmdlet to the variable $csrGenerationSpec. That way I can then use the content of the variable as input for the conversion to JSON. The first entry defines that this object will be used for generating the CSRs (csrGenerationSpec).

When performing the conversion, I am using a depth of 10. This is more than I need, but the default depth for JSON conversion is only 2, so if I am converting a system object with more than two levels, I would end up with a truncated object. By using 10, I am guaranteed to not accidentally truncate my data. Once the conversion has been done, I’m outputting the JSON to a file.

That last part performs the CSR request and I have added a do loop to track the progress of the request to prevent the script from executing the next part of the script until the CSR is successful.

# Create the JSON file for CSR Generation
logger "Creating JSON file for CSR request in $domainName"
$csrsGenerationSpec = New-Object -TypeName PSCustomObject 
$csrsGenerationSpec | Add-Member -NotePropertyName csrGenerationSpec -NotePropertyValue @{country = $country; email = $email; keyAlgorithm = $keyAlg; keySize = $keySize; locality = $locality; organization = $org; organizationUnit = $orgUnit; state = $state }
$csrsGenerationSpec | Add-Member -NotePropertyName resources -NotePropertyValue @(@{fqdn = $vcenter.fqdn; name = $vcenter.fqdn; sans = @($vcenter.fqdn); resourceID = $vcenter.id; type = $vcenter.type }, @{fqdn = $nsxTCluster.vipfqdn; name = $nsxTCluster.vipfqdn; sans = @($nsxTCluster.vip, $nsxTCluster.vipfqdn); resourceID = $nsxTCluster.id; type = $nsxTCluster.type }, @{fqdn = $sddcCertManager.fqdn; name = $sddcCertManager.fqdn; sans = @($sddcCertManager.fqdn); resourceID = $sddcCertManager.id; type = $sddcCertManager.type })

$csrsGenerationSpec | ConvertTo-Json -Depth 10 | Out-File -Filepath $jsonPathDir\csrsGenerationSpec.json

# Create CSRs for vCenter, NSX-T and SDDC Manager
logger "Requesting CSR's for $domainName"
$csrReq = Request-VCFCertificateCSR -domainName $domainName -json $jsonPathDir\csrsGenerationSpec.json
do { $taskStatus = Get-VCFTask -id $($csrReq.id) | Select-Object status; Start-Sleep 5 } until ($taskStatus -match "Successful")

With the CSR successful, we can now proceed to the creation of the certificates. The process is very similar to requesting the CSR. The format of the system object is also similar but in this case the property name is caType with a value of “Microsoft”. We then define the fqdn and the sans, followed by the resource id which will be matched with the CSR request for that object.

Output to JSON is the same. I then use the Request-VCFCertificate cmdlet with the json file to create the certificates. Again, I’m using a do loop to track and verify success.

# Create JSON Spec for requesting certificates
logger "Creating JSON spec for certificate creation in $domainName"
$certCreateSpec = New-Object -TypeName PSCustomObject 
$certCreateSpec | Add-Member -NotePropertyName caType -NotePropertyValue "Microsoft"
$certCreateSpec | Add-Member -NotePropertyName resources -NotePropertyValue @(@{fqdn = $vcenter.fqdn; name = $vcenter.fqdn; sans = @($vcenter.fqdn); resourceID = $vcenter.id; type = $vcenter.type }, @{fqdn = $nsxTCluster.vipfqdn; name = $nsxTCluster.vipfqdn; sans = @($nsxTCluster.vip, $nsxTCluster.vipfqdn); resourceID = $nsxTCluster.id; type = $nsxTCluster.type }, @{fqdn = $sddcCertManager.fqdn; name = $sddcCertManager.fqdn; sans = @($sddcCertManager.fqdn); resourceID = $sddcCertManager.id; type = $sddcCertManager.type })

$certCreateSpec | ConvertTo-Json -Depth 10 | Out-File -Filepath $jsonPathDir\certCreateSpec.json

# Request the creation of certificates for vCenter, NSX-T and SDDC Manager
logger "Generating Certs on CA for $domainName"
$certCreateReq = Request-VCFCertificate -domainName $domainName -json $jsonPathDir\certCreateSpec.json
do { $taskStatus = Get-VCFTask -id $($certCreateReq.id) | Select-Object status; Start-Sleep 5 } until ($taskStatus -match "Successful")

The last part of the script installs the certificates on the SDDC Manager, vCenter and NSX-T Manager. The process is the same as above, but this time the property name is operationType and the value is install. The rest of the object is the same as the certificate creation request.

Once the conversion has been completed, I use the Set-VCFCertificate cmdlet to install the certificates and once again I use a do loop to track progress and success.

# Create JSON Spec for installing certificates
logger "Creating JSON Spec for installing certificates"
$certInstallSpec = New-Object -TypeName PSCustomObject
$certInstallSpec | Add-Member -NotePropertyName operationType -NotePropertyValue "INSTALL"
$certInstallSpec | Add-Member -NotePropertyName resources -NotePropertyValue @(@{fqdn = $vcenter.fqdn; name = $vcenter.fqdn; sans = @($vcenter.fqdn); resourceID = $vcenter.id; type = $vcenter.type }, @{fqdn = $nsxTCluster.vipfqdn; name = $nsxTCluster.vipfqdn; sans = @($nsxTCluster.vip, $nsxTCluster.vipfqdn); resourceID = $nsxTCluster.id; type = $nsxTCluster.type }, @{fqdn = $sddcCertManager.fqdn; name = $sddcCertManager.fqdn; sans = @($sddcCertManager.fqdn); resourceID = $sddcCertManager.id; type = $sddcCertManager.type })

$certInstallSpec | ConvertTo-Json -Depth 10 | Out-File -Filepath $jsonPathDir\certInstallSpec.json

# Install certificates on vCenter, NSX-T and SDDC Manager
logger "Installing Certificates for $domainName"
$certInstallReq = Set-VCFCertificate -domainName $domainName -json $jsonPathDir\certInstallSpec.json
do { $taskStatus = Get-VCFTask -id $($certInstallReq.id) | Select-Object status; Start-Sleep 5 } until ($taskStatus -match "Successful")

Save the script in the directory of your choosing and then execute it from the PowerShell prompt. The logger function should open a separate window as well as creating a log file.

Once the script has completed execution, you can log into each of the components and check that the browser connection is secure

Alasdair Carnie on GithubAlasdair Carnie on Linkedin
Alasdair Carnie
Alasdair has been an IT professional for over 32 years. Starting out as a Network Engineer in the heady days of thin/thick wire Ethernet, token-ring and Novell Netware, he soon established himself as an authority on LAN/WAN technologies. Over the next 15 years he worked for many blue-chip companies, developing their network and network services infrastructures. In the late 90’s he entered the world of virtualization via a beta of VMware Workstation and soon became an expert in VMware’s technologies. He has spent the last 15 years designing, implementing and teaching virtual infrastructure, virtual networking and storage. He joined VMware in 2012 to help enable parnters on VMware technologies. He currently works as part of VMware’s Livefire team of SDDC / Multi-Cloud design, deploy and automation experts, where his current focuses are VMware Cloud Foundation, VMware Cloud Services and Kubernetes.

Leave a Reply

%d bloggers like this: