VMware vCenter with PowerShell

I’m trying to automate the process of creating and renewing TLS certificates for my VMware vCenter servers using Certify the web and PowerShell. I want to use PowerShell to push the certificates to the vCenter servers after they are created or renewed by Certify the web. Does anyone have any experience or tips on how to do this? Here is what I have so far:

param($result) # Required to access the $result parameter

#Loads all Module(s)
Try { Import-Module -Name VMware.PowerCLI -ErrorAction Stop }
Catch { Write-Host "Unable to load VMware.PowerCLI module, Please, run 'Install-Module -Name VMware.PowerCLI -AllowClobber -Force'" -ForegroundColor Red; Exit }

if ($result.IsSuccess) {
    # Edit Variables Below
    $FQDM               = "vcenter.vmware.com" # E.G. vcenter.vmware.com
    $vCenterUsername    = "username"
    $vCenterPassword    = "Your_Password"
    # Do Not Edit Below This Point

    # Setup to connect to a VMware vCenter.
    $vCenterConnection = Connect-VIServer -Server $FQDM -User $vCenterUsername -Password $vCenterPassword

    # Connect to a VMware vCenter
    $vCenterConnection

    # Getting new certs
    $certificatePem = Get-Content -Path "C:\CTW\FullChain\$($FQDM)\$($FQDM).pem" -Raw
    $certificatePrivKeyPem = Get-Content -Path "C:\CTW\FullChain\$($FQDM)\$($FQDM).privkey.pem" -Raw
    # You will need manualy push up CA cert(s)

    # Update the vCenter certificate
    Try { Set-VIMachineCertificate -PemCertificate $certificatePem -PemKey $certificatePrivKeyPem -ErrorAction Stop }
    Catch { Write-Host "Failed to update vCenter certificate. Error: $_" -ForegroundColor Red }

    # Cleans up TLS certs.
    Get-VITrustedCertificate | Where-Object { $_.NotValidAfter -lt (Get-Date) } | Remove-VITrustedCertificate
    
    # Disconnect from vCenter
    Disconnect-VIServer -Server $FQDM -Confirm:$false
}

I haven’t tried it but this looks good to me based on their documentation and Managing vSphere Certificates with PowerCLI - VMware PowerCLI Blog

I presume you have a Deploy to Generic Server task or a couple of Export Certificate tasks already to get the component files you need.

You may be able to use PowerShell Secrets or some other secrets vault to avoid storing the pwd in your script.

Do you currently have it working or or you hitting any problems?

At https://my-little-vcentre.example.co.uk there is a link to download Download trusted root CA certificates at the far right. You can pop the vCentre CA cert into Group Policy or /usr/local/shared/ca-whereverubuntuordebianputsthem/ and run update-ca-something. Anyway, browser CA trust is well documented when you work out what you are actually looking for!

You can’t grab a SSL cert from Lets Encrypt that you can use to mint your own certs (ie an intermediate CA), so I suggest you drop that line of enquiry.

It’s been a long time coming, but I finally got something that works…

#-----------------------------------------------#
#------CTW_vSphere_PowerShell_API---------------#
#------Author: Taylor D. Marchetta--------------#
#------Version 1.0.0----------------------------#
#-----------------------------------------------#

param($result)

# Variables to be set before invoking the script
$CertDir = "C:\CTW\Certs\" # C:\somefolder\
$Server = "https://sub.domain.org"
$Cred = New-Object System.Management.Automation.PSCredential("administrator@vsphere.local", (ConvertTo-SecureString "MyPassword" -AsPlainText -Force))

# Ignore SSL errors for these API requests
Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;

public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true; // Trust all certificates
    }
}
"@

# Set the custom certificate policy
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

# Set the security protocol to include TLS 1.2 and TLS 1.3
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13

# Get the session ID
Write-Output "Getting session ID..."
$sessionResponse = Invoke-RestMethod -Uri "$Server/rest/com/vmware/cis/session" -Method Post -Headers @{
    "Content-Type" = "application/json"
    "Accept" = "application/json"
    "vmware-use-header-authn" = "test"
    "vmware-api-session-id" = "null"
} -Credential $Cred

$SessionID = $sessionResponse.value
Write-Output "Session ID: $SessionID"

# Format the certificate and private key files to escape newline characters
$PrivKey = (Get-Content -Path "$CertDir\privkey.pem" -Raw) -replace "`r`n", "`n"
$Cert = (Get-Content -Path "$CertDir\cert.pem" -Raw) -replace "`r`n", "`n"
#$Root_cert = (Get-Content -Path "$CertDir\cachain.pem" -Raw) -replace "`r`n", "`n"

# Build the JSON request body
$RequestBody = @{
    spec = @{
        cert      = $Cert
        key       = $PrivKey
        #root_cert = $Root_cert
    }
} | ConvertTo-Json -Compress

# Update the certificate
Write-Output "Updating certificate..."
Invoke-RestMethod -Uri "$Server/rest/vcenter/certificate-management/vcenter/tls" -Method Put -Headers @{
    "vmware-api-session-id" = $SessionID
    "Content-Type" = "application/json"
} -Body $RequestBody 

# Close the session

# Output the RequestBody to check its contents
Write-Output $RequestBody

Write-Output "Closing session..."
Invoke-RestMethod -Uri "$Server/rest/com/vmware/cis/session" -Method Delete -Headers @{
    "vmware-api-session-id" = $SessionID
}

Write-Output "Certificate update completed!"
1 Like

Nice, I assume you’re using a deployment task like Deploy to Generic Server to export the certificate and private key files ready for upload to the API. IS your cert the fullchain.pem file or just the leaf cert? Often services need the fullchain so that they serve the intermediate. Windows is quite good at working around missing intermediates but other OSes tend to get upset.