SSO 5.5 HA Automated with PowerShell 4

A short while back VMware released a series of videos detailing the process of achieving a load balanced VMware Single Sign-On 5.5 High Availability (HA) configuration using VMware vCNS, F5 BIG-IP or Citrix Netscalar.

Part of the process to achieve such a configuration is to update the various Single Sign-On services with with your own custom certificates which can be quite tedious so I’ve taken the liberty to automate much of the certificate replacement process using powershell.

Here is an example architecture of an SSO 5.5 HA configuration (picture courtesy of VMware)

sso55-ha-example-architecture

The script should be fairly self-explanatory but there is a few things I would like to highlight:

  • It was tested on a standard Windows Server 2012 installation
  • The script assumes you have 2x VMware SSO 5.5 servers already installed & configured to be part of the same site (vCenter Single Sign-On for an additional vCenter Server in an existing site)
  • On 1st run you’ll be prompted to confirm the values saved and then presented with a menu
  • You have the option to generate your own CA root certificates
  • You must run the script directly on SSO1 & SSO2:
  1. Generate & replace the certificates on ‘SSO1′,
  2. Copy C:\Certs (by default) over to SSO2
  3. Replace the certificates on SSO2
  • Continue to configure your load balancer (not part of this blog/script)

Here is how it looks like on 1st run:

sso55-ha-script-menu

The script:

# USAGE:
#	1) Set the powershell execution policy: Set-ExecutionPolicy Unrestricted -force
#   2) Update the variables in the script below
# 	3) Run it: ./sso55-ha-automation-script.ps1
#	4) Create the certificates
# 	5) Replace the certificates on 'SSO1'
#	6) Copy the C:\Certs directory over to 'SSO2'
#   7) Replace the certificates on 'SSO2'
#
# WHATS NEW:
# v1.0 - initial release
# v1.1 - updated with openssl v0.9.8zb download link
#      - tested with vsphere 5.5 update 1c (build 1945274)
# 
# AUTHOR:
# Andre Smith - pshell.info
######################################

################################
# SSO 5.5 HA Automation Script # <-- CHANGE ME!
################################

# --- SSO Variables  ---

$sso1_fqdn = "sso1.vmware.local"
	# primary sso server fqdn
	# must match the server's hostname
	# will be included in the custom sso certificate
	# eg: "sso1.vmware.local"

$sso2_fqdn = "sso2.vmware.local"
	# secondary SSO server fqdn
	# must match the server's hostname
	# will be included in the custom sso certificate
	# set to $null if not needed
	# eg: "sso2.vmware.local"

$sso_lb_fqdn = "sso.vmware.local"
	# load balancer fqdn for the sso lookup service
	# will set as the SSO lookup service entry point
	# eg: "sso.vmware.local"

$sso_lb_vip = "192.168.110.40"
	# sso load balancer IP address
	# specifying the IP will have it included in the SSO certificate
	# set to $null have it excluded
	# eg: "192.168.110.40"

$sso_admin_user = "administrator@vsphere.local"
	# default sso administrator username

$sso_admin_password = "MyCompl3x.SSOPassword"
	# the sso password that was used for the installation

$workdir = "C:\Certs"
	# directory where sso certificates will be saved

# --- CA Root Certifiate Attributes  ---

$cacert = "cacert.pem"
    # The CA Root Certificate
	# Copy yours to $workdir if you want to bring your own

$cakey = "cakey.pem"
    # The CA Root Certificate Key
	# Copy yours to $workdir if you want to bring your own

# --- Certifiate Attributes  ---

$country    = "US"
$state      = "Texas"
$city       = "Austin"
$company    = "ACME Corporation"
$email 		= "helpdesk@acme.corp"

$orgunit    = "vCenterSSO" + "_" + (Get-Date -Format yyyyMMdd_HHmm) # <---- SSO Specific
$commonname = $sso_lb_fqdn                                          # <---- SSO Specific

###########################
# MAIN SCRIPT STARTS HERE #  <-- MODIFY AT OWN RISK
###########################

# --- Functions starts here  ---

function Download-Manager ($url, $dest) {
	$wc = new-object system.net.webclient
	$wc.downloadfile($url, $dest)
}

function Display-Toolkitmenu {

	[int]$ToolkitMenuChoice = 0
	while ($ToolkitMenuChoice -lt 1 -or $ToolkitMenuChoice -gt 8 ) {
	Write-Host ""
	Write-Host "Welcome to the SSO 5.5 HA Automation Script" -ForegroundColor Green
	Write-Host ""
	Write-Host "--- Misc: ---" -ForegroundColor Green
	Write-Host "1. Print Installation Variables"
	Write-Host "2. Download & Install OpenSSL v0.9.8zb"
	Write-Host ""
	Write-Host "--- Single Sign-On: ---" -ForegroundColor Green
	Write-Host "3. Generate CA Root Certificates"
	Write-Host "4. Generate SSO Certificates"
	Write-Host "5. Replace SSO1 Certificates"
	Write-Host "6. Replace SSO2 Certificates (HA Configuration)"
	Write-Host "7. List SSO LookupServices"
	Write-Host ""
	Write-Host "8. Quit"
	Write-Host ""

	[Int]$ToolkitMenuChoice = read-host "Please enter an option 1 to 8"
	}

	Switch ( $ToolkitMenuChoice ) {
		1 {Print-Script-Variables; Display-ToolkitMenu}
        2 {Install-OpenSSL; Display-ToolkitMenu}
		3 {Generate-CA-Root-Certs; Display-ToolkitMenu}
		4 {Generate-SSO-Certs; Display-ToolkitMenu}
		5 {Replace-SSO1-Certs; Display-ToolkitMenu}
		6 {Replace-SSO2-Certs; Display-ToolkitMenu}
		7 {List-SSO-Services; Display-ToolkitMenu}
		8 {Exit}
	default {Exit}
	}
}

function Print-Script-Variables {
	Write-Host " "
	Write-Host "--- SSO : ---" -ForegroundColor Yellow
	Write-Host "SSO1 FQDN      = $sso1_fqdn" -ForegroundColor Yellow
	if ($sso2_fqdn -ne $null) {
	Write-Host "SSO2 FQDN      = $sso2_fqdn" -ForegroundColor Yellow
	}
	Write-Host "SSO-LB FQDN    = $sso_lb_fqdn" -ForegroundColor Yellow
	if ($sso_lb_vip -ne $null) {
	Write-Host "SSO-LB IP      = $sso_lb_vip" -ForegroundColor Yellow
	}
	Write-Host "SSO Admin User = $sso_admin_user" -ForegroundColor Yellow
	Write-Host "SSO Admin Pass = $sso_admin_password" -ForegroundColor Yellow
	Write-Host " "
	Write-Host "--- CERTIFICATE ATTRIBUTES : ---" -ForegroundColor Yellow
	Write-Host "Country           = $country" -ForegroundColor Yellow
	Write-Host "State             = $state" -ForegroundColor Yellow
	Write-Host "City              = $city" -ForegroundColor Yellow
	Write-Host "Company           = $company" -ForegroundColor Yellow
	Write-Host "Commonname (CA)   = $company CA" -ForegroundColor Yellow
	Write-Host "Commonname (SSO)  = $commonname" -ForegroundColor Yellow
	Write-Host "Organization Unit = $orgunit" -ForegroundColor Yellow
	Write-Host "E-mail            = $email" -ForegroundColor Yellow

	$title = "Confirm Y/N"
	$message = "Are these values correct?"
	$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Are these values correct?"
	$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Abort."
	$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
	$result = $host.ui.PromptForChoice($title, $message, $options, 0)
	switch ($result)
    {
        0 {Display-Toolkitmenu}
        1 {exit}
    }
}

function Install-OpenSSL {

	$openssl_installer = "Win32OpenSSL-0_9_8zb.exe"

	Write-Host "`n--> Downloading & Installing OpenSSL v0.9.8zb ..."  -ForegroundColor Yellow

	# Downloading OpenSSL
	Download-Manager -url "http://slproweb.com/download/Win32OpenSSL_Light-0_9_8zb.exe" -dest (Join-Path $env:TEMP $openssl_installer)

	# Installing OpenSSL
	if (!(Test-Path (Join-Path $env:TEMP $openssl_installer))) {
		Write-Host "`n--> ERROR: $openssl_installer Installer not found, aborting ..." -BackgroundColor Red
        pause
		return
	}

	else {
		$sslexe = (Join-Path $env:TEMP $openssl_installer)
		# cmd /c $sslexe /silent
		cmd /c $sslexe /silent /verysilent /sp- /suppressmsgboxes
		$env:Path = $env:Path + ";C:\OpenSSL;C:\OpenSSL\bin"
	}

	# Testing
	if (Test-Path C:\OpenSSL\bin\openssl.exe) {
		Write-Host "`n--> OpenSSL was successfully installed ..."  -ForegroundColor Yellow
        pause
	}
	else {
		Write-Host "`n--> ERROR: Looks like the OpenSSL installation was unsuccessful, aborting ..." -BackgroundColor Red
        pause
		return
	}
}

function Generate-CA-Root-Certs {

	# Error checking
	Write-Host "`n--> Running a few checks ..."  -ForegroundColor Yellow
	sleep 1

	$opensslexe = "C:\OpenSSL\bin\openssl.exe"

	if (( Test-Path $opensslexe) -eq $False ) {
		Write-Host "`n--> ERROR: $opensslexe not found, aborting ..." -BackgroundColor Red
		pause
		return
	}

	# Confirming the working directory
    if (!(Test-Path($workdir))) {
		$null = New-Item -Type Directory $workdir
	}

	# Creating the CA Root Certificates
	Write-Host "`n--> Creating new Root CA certificates ...`n"  -ForegroundColor Yellow

	New-Alias OpenSSL $opensslexe
	Push-Location -Path $workdir

	# http://www.mad-hacking.net/documentation/linux/security/ssl-tls/creating-ca.xml
	$root_ca_name = $company + " CA"

$ca_cnf = "
HOME			   = .

[ ca ]
default_ca	= CA_default

[ CA_default ]

dir				 = ./$root_ca_name			# Where everything is kept
certs	         = ./certs		 # Where the issued certs are kept
new_certs_dir	 = ./newcerts		# default place for new certs
certificate	     = ./$root_ca_name/cacert.pem 	# The CA certificate
private_key      =  ./$root_ca_name/cakey.pem

database         =  ./$root_ca_name/database/index.txt
serial           =  ./$root_ca_name/database/serial
crl_dir          =  ./$root_ca_name/revoked
crlnumber        =  ./$root_ca_name/crlnumber

default_days	 = 3650			# how long to certify for
default_crl_days = 30			# how long before next CRL
default_md	     = sha1			# which md to use.
preserve	     = no			# keep passed DN ordering

name_opt 	     = ca_default		# Subject Name options
cert_opt 	     = ca_default		# Certificate field options
preserve         = no
email_in_dn      = no

[ policy_match ]
countryName             = supplied
stateOrProvinceName     = supplied
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ crl_ext ]
authorityKeyIdentifier = keyid:always,issuer:always

[ usr_cert ]
basicConstraints       = CA:FALSE
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

[ req ]
default_md = sha512
default_bits = 2048
default_keyfile = rui.key
distinguished_name = req_distinguished_name
encrypt_key = no
string_mask = nombstr
req_extensions = v3_ca_req

[ v3_ca_req ]
basicConstraints       = critical, CA:TRUE, pathlen:0
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always, issuer:always
keyUsage               = keyCertSign, cRLSign
nsCertType             = sslCA, emailCA, objCA

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default	= $country

stateOrProvinceName	= State or Province Name (full name)
stateOrProvinceName_default	= $state

localityName			= Locality Name (eg, city)
localityName_default = $city

0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= $company

commonName			= Common Name (eg, YOUR name)
commonName_default = $company CA

emailAddress			= Email Address
emailAddress_default 	= $email
"

	# Saving the ca cert config to file
	$cacert_cnf = (Join-Path $workdir "cacert.cnf")
	Set-Content -Path $cacert_cnf -Value $ca_cnf

	# Generate a new root certificate
	OpenSSL req -new -x509 -extensions v3_ca_req -keyout $cakey -out $cacert -days 3650 -config $cacert_cnf -batch

	# Removing the alias
	Remove-Item alias:OpenSSL
	Pop-Location

	Write-Host "`n--> CA Root Certificates saved to $workdir ...`n"  -ForegroundColor Yellow
	pause
}

function Generate-SSO-Certs {

	# error checking
	Write-Host "`n--> Running a few checks ..."  -ForegroundColor Yellow
	sleep 1

	$opensslexe = "C:\OpenSSL\bin\openssl.exe"

	if (( Test-Path $opensslexe) -eq $False ) {
		Write-Host "`n--> ERROR: $opensslexe not found, aborting ..." -BackgroundColor Red
        pause
		return
	}

	# Confirming the working directory
    if (!(Test-Path($workdir))) {
		$null = New-Item -Type Directory $workdir
	}

    # looking if it can find the ca certs
	if ( (  (test-path (Join-Path $workdir $cakey) ) -and (test-path (Join-Path $workdir $cacert) )  ) -eq $False) {
	    Write-Host "`n--> ERROR: CA Root certificates was not found in $workdir ..." -BackgroundColor Red
	    Write-Host "--> ERROR: Unable to generate the SSO certificates, aborting ..." -BackgroundColor Red
        pause
		return
	}

    # Generating the SSO certificates
	Write-Host "`n--> Generating the SSO certificates ...`n"  -ForegroundColor Yellow

	New-Alias OpenSSL $opensslexe
	Push-Location -Path $workdir

	# declaring a few more variables needed for the certificates
	$subject_alt_name = "DNS:" + $sso1_fqdn + ", DNS:" + $sso1_fqdn.Split(".")[0] + ", DNS:" + $sso2_fqdn + ", DNS:" + $sso2_fqdn.Split(".")[0] + ", DNS:" + $sso_lb_fqdn

	if ($sso_lb_vip -ne $null) {
		$subject_alt_name = $subject_alt_name + ", IP:" + $sso_lb_vip
	}

	$ssocert_cnf = (Join-Path $workdir "$commonname.cnf")

# creating the sso configuration file
$sso_cnf = "
# OpenSSL configuration file for SSO certificate generation
# This definition stops the following lines choking if HOME isn't  defined.
HOME			   = .
RANDFILE		   = ./.rnd

[ req ]
default_md = sha512
default_bits = 2048
default_keyfile = rui.key
distinguished_name = req_distinguished_name
encrypt_key = no
string_mask = nombstr
req_extensions = v3_req

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = $subject_alt_name

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default	= $country

stateOrProvinceName	= State or Province Name (full name)
stateOrProvinceName_default	= $state

localityName			= Locality Name (eg, city)
localityName_default = $city

0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= $company

organizationalUnitName		      = Organizational Unit Name (eg, section)
organizationalUnitName_default	= $orgunit

commonName			= Common Name (eg, YOUR name)
commonName_default = $commonname

emailAddress			= Email Address
emailAddress_default = $email
"

    # Writing the configuration out to file
    Set-Content -Path $ssocert_cnf -Value $sso_cnf

    # Creating the certificate request & exporting the private key
    OpenSSL req -new -nodes -out "$commonname.csr" -keyout "$commonname-orig.key" -config $ssocert_cnf -batch

    # Converting the key into proper RSA format
    OpenSSL rsa -in "$commonname-orig.key" -out "$commonname.key"

    # Generate the certificate
    # OpenSSL x509 -req -in "$commonname.csr" -CA "$cacert" -CAkey "$cakey" -CAcreateserial -out "$commonname.crt" -days 7300 -extfile $ssocert_cnf -extensions v3_req -passin pass:$capass
    OpenSSL x509 -req -in "$commonname.csr" -CA "$cacert" -CAkey "$cakey" -CAcreateserial -out "$commonname.crt" -days 7300 -extfile $ssocert_cnf -extensions v3_req

    # Creating a P12 archive file
    OpenSSL pkcs12 -export -in "$commonname.crt" -inkey "$commonname.key" -certfile "$cacert" -name "ssoserver" -passout pass:changeme -out "$commonname.p12"

	# Removing the alias
	Remove-Item alias:OpenSSL
	Pop-Location

    Write-Host "`n--> SSO Certificates saved to $workdir ...`n"  -ForegroundColor Yellow
	pause

}

function Replace-SSO1-Certs {

	# error checking
	Write-Host "`n--> Running a few checks ..."  -ForegroundColor Yellow
	sleep 1

	if  ( (([System.Net.Dns]::GetHostByName((hostname)).HostName).ToLower()) -ne $sso1_fqdn ) {
		Write-Host "`n--> ERROR: Hostname mismatch" -BackgroundColor Red
		Write-Host "--> ERROR: Try replacing the SSO certificate on $sso1_fqdn instead, aborting ..." -BackgroundColor Red
		pause
		return
	}

	if ( (Test-Path "c:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components\bin") -eq $false ) {
		Write-Host "`n--> ERROR: SSO does not seem to be installed - maybe install it first? Aborting ..." -BackgroundColor Red
		pause
		return
	}

	if ( (  (test-path (Join-Path $workdir $cakey) ) -and (test-path (Join-Path $workdir "$sso_lb_fqdn.p12") )  ) -eq $False) {
		Write-Host "`n--> ERROR: Certificates not found at $workdir, aborting ..." -BackgroundColor Red
		pause
		return
	}

	# continuing script ...
    Push-Location -Path "c:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components\bin"

	Write-Host "`n--> Creating Java Key Store file root-trust.jks ..."  -ForegroundColor Yellow
	sleep 1
    .\keytool.exe -v -importkeystore -srckeystore (join-path $workdir "$sso_lb_fqdn.p12") -srcstoretype pkcs12 -srcstorepass changeme -srcalias ssoserver -destkeystore (Join-Path $workdir root-trust.jks) -deststoretype JKS -deststorepass testpassword -destkeypass testpassword

	Write-Host "`n--> Adding the root certificate to the Java Key Store ..."  -ForegroundColor Yellow
	sleep 1
    "yes" | .\keytool.exe -v -importcert -keystore (Join-Path $workdir "root-trust.jks") -deststoretype JKS -storepass testpassword -keypass testpassword -file (Join-Path $workdir $cacert) -alias root-ca

    Pop-Location

	Write-Host "`n--> Copying Java Keystore to the required Java Keystore name ..."  -ForegroundColor Yellow
	sleep 1
    Copy-Item (Join-Path $workdir "root-trust.jks") (Join-Path $workdir "server-identity.jks")

	Write-Host "`n--> Setting environment variables ..."  -ForegroundColor Yellow
	sleep 1
    $env:JAVA_HOME="C:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components"
    $env:Path = $env:Path + ";C:\Program Files\VMware\Infrastructure\VMware\CIS\vmware-sso;" + $env:JAVA_HOME + "\bin"

    New-Alias OpenSSL "C:\OpenSSL\bin\openssl.exe"

	Write-Host "`n--> Registering the CA root certificate with the VMware Trust Store ..."  -ForegroundColor Yellow
	sleep 1
    $hash =  OpenSSL x509 -noout -subject_hash -in (Join-Path $workdir $cacert)
	sleep 1

	Write-Host "`n--> Creating the VMware SSL Directory ..."  -ForegroundColor Yellow
	sleep 1
    $null = New-Item -Type Directory "C:\ProgramData\VMware\SSL"
    sleep 1

	Write-Host "`n--> Copying CA root certificate hash to the VMware SSL Directory as $hash.0 ..."  -ForegroundColor Yellow
	sleep 1
    Copy-Item (Join-Path $workdir $cacert) "C:\ProgramData\VMware\SSL\$hash.0"

    Write-Host "`n--> Copying CA root certificate to the VMware SSL folder as ca_certificates.crt ..."  -ForegroundColor Yellow
	sleep 1
	Copy-Item (Join-Path $workdir $cacert) "C:\ProgramData\VMware\SSL\ca_certificates.crt"

	Write-Host "`n--> Creating SSO 'property' files ..."  -ForegroundColor Yellow
	sleep 1
	$ssl_path = (Join-Path $workdir $cacert)

$sso_admin_properties = "[service]
friendlyName=The administrative interface of the SSO server
version=1.5
ownerId=
productId=product:sso
type=urn:sso:admin
description=The administrative interface of the SSO server

[endpoint0]
uri=https://$sso_lb_fqdn`:7444/sso-adminserver/sdk/vsphere.local
ssl=$ssl_path
protocol=vmomi"

    Set-Content -Path (Join-Path $workdir "admin.properties") -Value $sso_admin_properties

$sso_gc_properties = "[service]
friendlyName=The group check interface of the SSO server
version=1.5
ownerId=
productId=product:sso
type=urn:sso:groupcheck
description=The group check interface of the SSO server

[endpoint0]
uri=https://$sso_lb_fqdn`:7444/sso-adminserver/sdk/vsphere.local
ssl=$ssl_path
protocol=vmomi"

	Set-Content -Path (Join-Path $workdir "gc.properties") -Value $sso_gc_properties

$sso_sts_properties = "[service]
friendlyName=STS for Single Sign On
version=1.5
ownerId=
productId=product:sso
type=urn:sso:sts
description=The Security Token Service of the Single Sign On server.

[endpoint0]
uri=https://$sso_lb_fqdn`:7444/ims/STSService/vsphere.local
ssl=$ssl_path
protocol=wsTrust"

Set-Content -Path (Join-Path $workdir "sts.properties") -Value $sso_sts_properties

	Write-Host "`n--> Creating SSO 'id' files ..."  -ForegroundColor Yellow
	sleep 1

    # grabbing the output of listServices
    $sso_services_output = ssolscli listServices https://$sso1_fqdn`:7444/lookupservice/sdk

    # converting the output to an index
    $sso_services_index = @()
    foreach ($line in $sso_services_output) {
            $sso_services_index += "$line"
    }

    # finding the serviceID for each of the sso services & saving it to a file
    $gc_id = $sso_services_index[ $sso_services_index.IndexOf( "type=urn:sso:groupcheck" ) -2 ].split("=")[1]
    Set-Content -Path (Join-Path $workdir "gc_id")  -Value $gc_id

    $admin_id = $sso_services_index[ $sso_services_index.IndexOf( "type=urn:sso:admin" ) -2 ].split("=")[1]
    Set-Content -Path (Join-Path $workdir "admin_id")  -Value $admin_id

    $sts_id = $sso_services_index[ $sso_services_index.IndexOf( "type=urn:sso:sts" ) -2 ].split("=")[1]
    Set-Content -Path (Join-Path $workdir "sts_id")  -Value $sts_id

	Write-Host "`n--> Backing up STS certificate files & copying the new certificates ..."  -ForegroundColor Yellow
	sleep 1

	# backing up sts certificate files
	$VMwareSTSconf = "C:\ProgramData\VMware\CIS\runtime\VMwareSTS\conf"
    $null = New-Item -Type Directory (Join-Path $VMwareSTSconf "backup")
    Copy-Item (Join-Path $VMwareSTSconf "ssoserver.p12") (Join-Path $VMwareSTSconf "backup")
    Copy-Item (Join-Path $VMwareSTSconf "ssoserver.crt") (Join-Path $VMwareSTSconf "backup")
    Copy-Item (Join-Path $VMwareSTSconf "ssoserver.key") (Join-Path $VMwareSTSconf "backup")

    # copying new certificates to the correct destination
    Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.p12") (Join-Path $VMwareSTSconf "ssoserver.p12")
    Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.crt") (Join-Path $VMwareSTSconf "ssoserver.crt")
    Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.key") (Join-Path $VMwareSTSconf "ssoserver.key")

	Write-Host "`n--> Adding hosts file entry for $sso_lb_fqdn to point to the localhost ..."  -ForegroundColor Yellow
	sleep 1

    # $hostsfile_entry = (Get-NetIPAddress -InterfaceAlias Public -AddressFamily IPv4).IPAddress + "    $sso_lb_fqdn"
	$hostsfile_entry = "127.0.0.1    $sso_lb_fqdn"
    Add-Content C:\Windows\System32\Drivers\etc\hosts $hostsfile_entry

	Write-Host "`n--> Updating the 3x SSO services ...            <-- THIS COULD TAKE A MINUTE, HANG IN THERE"  -ForegroundColor Yellow
	sleep 1
    $gc_update_results = ssolscli  updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir gc_id) -ip ( Join-Path $workdir gc.properties)
	if ( ($gc_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (1) SSO GroupCheck service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO GroupCheck service update failed, aborting ..." -BackgroundColor Red
        pause
		return
	}

    # updating admin
    $admin_update_results = ssolscli updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir admin_id) -ip ( Join-Path $workdir admin.properties)
	if (($admin_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (2) SSO Admin service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO Admin service update failed, aborting ..." -BackgroundColor Red
        pause
		return
	}

    # restarting sts
	Write-Host "`n--> Restarting the VMware STS service before updating STS ...`n"  -ForegroundColor Yellow
	sleep 1
    net stop VMwareSTS
    net start VMwareSTS

    # updating sts
	Write-Host "`n--> Updating the VMware STS service ..."  -ForegroundColor Yellow
    $sts_update_results =ssolscli updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir sts_id) -ip ( Join-Path $workdir sts.properties)
	if (($sts_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (3) SSO STS service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO STS service update failed, aborting ..." -BackgroundColor Red
        pause
		return
	}

	Write-Host "`n--> Removing hosts file entry ..."  -ForegroundColor Yellow
	sleep 1
    (Get-Content C:\Windows\System32\Drivers\etc\hosts) | ForEach-Object { $_ -replace $hostsfile_entry, ""  } | Set-Content C:\Windows\System32\Drivers\etc\hosts

	Write-Host "`n--> SSO services successfully updated :)"  -ForegroundColor Yellow
    pause
}

function Replace-SSO2-Certs {

	# error checking
	Write-Host "`n--> Running a few checks ..."  -ForegroundColor Yellow
	sleep 1

	if  ( (([System.Net.Dns]::GetHostByName((hostname)).HostName).ToLower()) -ne $sso2_fqdn ) {
	Write-Host "`n--> ERROR: Hostname mismatch" -BackgroundColor Red
	Write-Host "--> ERROR: Try replacing the SSO certificate on $sso2_fqdn instead, aborting ..." -BackgroundColor Red
    pause
	return
	}

	if ( (Test-Path "c:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components\bin") -eq $false ) {
		Write-Host "`n--> ERROR: SSO does not seem to be installed - maybe install it first? Aborting ..." -BackgroundColor Red
        pause
		return
	}

	if ( (  (test-path (Join-Path $workdir $cakey) ) -and (test-path (Join-Path $workdir "$sso_lb_fqdn.p12") )  ) -eq $False) {
		Write-Host "`n--> ERROR: Certificates not found at $workdir, aborting ..." -BackgroundColor Red
        pause
		return
	}

	# replacing sso2 certificates
	Write-Host "`n--> Setting environment variables ..."  -ForegroundColor Yellow
	sleep 1
    $env:JAVA_HOME="C:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components"
    $env:Path = $env:Path + ";C:\Program Files\VMware\Infrastructure\VMware\CIS\vmware-sso;" + $env:JAVA_HOME + "\bin"

	Write-Host "`n--> Adding hosts file entry for $sso_lb_fqdn to point to the localhost ..."  -ForegroundColor Yellow
	sleep 1

    # $hostsfile_entry = (Get-NetIPAddress -InterfaceAlias Public -AddressFamily IPv4).IPAddress + "    $sso_lb_fqdn"
	$hostsfile_entry = "127.0.0.1    $sso_lb_fqdn"
    Add-Content C:\Windows\System32\Drivers\etc\hosts $hostsfile_entry

	Write-Host "`n--> Copying the new VMware STS certificate files ..."  -ForegroundColor Yellow
	$VMwareSTSconf = "C:\ProgramData\VMware\CIS\runtime\VMwareSTS\conf"
	Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.p12") (Join-Path $VMwareSTSconf "ssoserver.p12")
    Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.crt") (Join-Path $VMwareSTSconf "ssoserver.crt")
    Copy-Item ( Join-Path $workdir "$sso_lb_fqdn.key") (Join-Path $VMwareSTSconf "ssoserver.key")

	 # restarting VMwareSTS
	Write-Host "`n--> Restarting the VMware STS service to accept the updated certificates ...`n"  -ForegroundColor Yellow
	sleep 1
    net stop VMwareSTS
    net start VMwareSTS

	Write-Host "`n--> Updating the 3x SSO services ...                  <-- THIS COULD TAKE A MINUTE, HANG IN THERE"  -ForegroundColor Yellow
	sleep 1
    $gc_update_results = ssolscli  updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir gc_id) -ip ( Join-Path $workdir gc.properties)
	if ( ($gc_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (1) SSO GroupCheck service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO GroupCheck service update failed, aborting ..." -BackgroundColor Red
        pause
		return
	}

    # updating admin
    $admin_update_results = ssolscli updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir admin_id) -ip ( Join-Path $workdir admin.properties)
	if (($admin_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (2) SSO Admin service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO Admin service update failed, aborting ..." -BackgroundColor Red
		pause
        return
	}

	# updating sts
    $sts_update_results =ssolscli updateService -d https://$sso1_fqdn`:7444/lookupservice/sdk -u administrator@vsphere.local -p $sso_admin_password -si (join-path $workdir sts_id) -ip ( Join-Path $workdir sts.properties)
	if (($sts_update_results.Contains("Return code is: Success")) -eq $true )  {
		Write-Host "`n    (3) SSO STS service was updated successfully ... "  -ForegroundColor Yellow
	}
	else {
		Write-Host "`n--> ERROR: SSO STS service update failed, aborting ..." -BackgroundColor Red
        pause
		return
	}

	Write-Host "`n--> Removing hosts file entry ..."  -ForegroundColor Yellow
	sleep 1
    (Get-Content C:\Windows\System32\Drivers\etc\hosts) | ForEach-Object { $_ -replace $hostsfile_entry, ""  } | Set-Content C:\Windows\System32\Drivers\etc\hosts

	Write-Host "`n--> SSO services was successfully updated :)"  -ForegroundColor Yellow
    pause

}

function List-SSO-Services {

	# error checking
	Write-Host "`n--> Running a few checks ..."  -ForegroundColor Yellow
	sleep 1

	if ( (Test-Path "c:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components\bin") -eq $false ) {
		Write-Host "`n--> ERROR: SSO does not seem to be installed - maybe install it first? Aborting ..." -BackgroundColor Red
        pause
		return
	}

	Write-Host "`n--> Setting environment variables ..."  -ForegroundColor Yellow
	sleep 1
    $env:JAVA_HOME="C:\Program Files\Common Files\VMware\VMware vCenter Server - Java Components"
    $env:Path = $env:Path + ";C:\Program Files\VMware\Infrastructure\VMware\CIS\vmware-sso;" + $env:JAVA_HOME + "\bin"

	Write-Host "`n--> Adding hosts file entry for $sso_lb_fqdn to point to the localhost ..."  -ForegroundColor Yellow
	sleep 1

    # $hostsfile_entry = (Get-NetIPAddress -InterfaceAlias Public -AddressFamily IPv4).IPAddress + "    $sso_lb_fqdn"
	$hostsfile_entry = "127.0.0.1    $sso_lb_fqdn"
    Add-Content C:\Windows\System32\Drivers\etc\hosts $hostsfile_entry

	Write-Host "`n--> Getting SSO LookupService Endpoints ...`n"  -ForegroundColor Yellow
	sleep 1
	$host_fqdn = (([System.Net.Dns]::GetHostByName((hostname)).HostName).ToLower())
	ssolscli listServices https://$host_fqdn`:7444/lookupservice/sdk
	pause

	Write-Host "`n--> Removing hosts file entry ..."  -ForegroundColor Yellow
	sleep 1
    (Get-Content C:\Windows\System32\Drivers\etc\hosts) | ForEach-Object { $_ -replace $hostsfile_entry, ""  } | Set-Content C:\Windows\System32\Drivers\etc\hosts

}

Print-Script-Variables

# END OF SCRIPT

Links / resources:

http://blogs.vmware.com/vsphere/2013/12/load-balancing-vcenter-single-sign-on.html
http://blogs.vmware.com/vsphere/2014/01/when-to-centralize-vcenter-single-sign-on-server-5-5.html
http://www.vmware.com/files/pdf/vcenter/VMware-vCenter-Server-5.5-Technical-Whitepaper.pdf

Comments & suggestions are always welcome.

Andre.

4 thoughts on “SSO 5.5 HA Automated with PowerShell

  1. Reply Christopher McCann Mar 20,2014 2:21 pm

    Really nice script. Personally I would have liked to see you be able to interact with VMware’s SSL Automation Tool (for consistency for clients), but this is really awesome! Takes all the guesswork out of the process.

    Nice Job!

    • Reply andre@pshell.info Mar 29,2014 7:36 am

      Thank you Christopher! Unfortunately I was unable to get SSO configured in HA mode using VMware’s SSL Automation Tool and decided to forego that option, but if more details emerge on that process then it is certainly something I’m willing to investigate further. If you only have a single SSO server who’s certificates you would like to replace then my script can accommodate that too – just set SSO2 to $null.

  2. Reply Feidhlim O'Leary Mar 25,2014 12:40 pm

    Hi Andre,

    Great script there. Just one issue I can see is that you appear to performing the ssolscli updateService commands in the order of 1) GroupCheck, 2) Admin, 3) STS.

    This should be in the order 1) STS, 2) Admin, 3) GroupCheck.
    Other orders have been seen to either fail or have subsequent issues down the line.

    You’ll see the same correct order in the following KB
    http://kb.vmware.com/kb/2058838

    Other than that great job. Will be very useful to use in the future.

    Thanks,
    Feidhlim

    • Reply andre@pshell.info Mar 29,2014 7:29 am

      Thank you Feidhlim! I was fortunate enough to be given early access to the whitepaper “VMware® vCenter Server™ 5.5 Deploying a centralized vCenter Single Sign-On server with a Network Load Balancer (NLB) Technical Reference” (mentioned here http://goo.gl/3At0xV) and based the order on that. I’m more than happy to change the order if needed. What issues have you’ve seen when using a different order?

Leave a Reply to Christopher McCann Cancel reply