Citrix Provisioning Services 5.6 automation

Other automation blogs I posted :
XenApp 6 Automation
XenDesktop 5 Automation
XenApp 4.5 / 5 Automation

Today, I needed to prepare the deployment of Citrix PVS servers in silent mode, as usual I’m using a distribution / deployment software like Altiris or SCCM to use the scripts you will read in this blog. The following scripts have been found on the Citrix Community and it was so well done by Kevin Bacon I didn’t want and need to build another one from scratch. To deploy the console and the PVS server, we use PowerShell because I needed to add as well a Windows Feature .NET Framework 3.5.1 before begining the Provisioning Services Server deployment. I bring some modification to these scripts to auto install the .Net Framework Feature, add log files for the installation troubleshooting and the PVS console installation.

Unattended installation and PVS farm creation

This first cmd script is the one to run to start the silent installation :

@ECHO OFF
CLS
Echo Enabling PowerShell Scripts ...
reg add HKLM\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell /v ExecutionPolicy /d Unrestricted /f
Echo Executing Build Script ...
%windir%\system32\WindowsPowerShell\v1.0\powershell -nologo "& ""c:\PVS_Auto_Install\Framework.ps1"""
%windir%\system32\WindowsPowerShell\v1.0\powershell -nologo "& ""c:\PVS_Auto_Install\PVSsilent.ps1"""
Echo Build Complete
PAUSE

This PowerShell very short script add the Framework 3.5.1 feature and is launch by the PVSinit.cmd launch script. (Thanx to Joe Shonk IV)

Import-Module Servermanager
Add-WindowsFeature NET-Framework-Core

and this is the big piece and thanks again to Kevin for this work, you will need two tools from the Microsoft Resource Kit ntrights.exe and subinacl.exe.

# Pre-reqs:
#   Install PowerShell
#   Install Framework 3.5.1 Feature
#   set-executionpolicy bypass -force
#   Include all local sites and treat unc paths as local intranet
 
# Constants
$installer_srv = "c:\pvs_auto_install\PVS_Server_x64.exe"
$installer_cons = "c:\pvs_auto_install\PVS_Console_x64.exe"
$ntrights = "c:\pvs_auto_install\ntrights.exe"
$subinacl = "c:\pvs_auto_install\subinacl.exe"
 
# Variables
$dbServer = Read-Host "Please enter the database server name (or name,port)"
#$dbInstance = Read-Host "Please enter the database instance name"
$dbName = Read-Host "Please enter the database name"
$siteName = Read-Host "Please enter the site name"
$svcUser = Read-Host "Please enter the service account user name (e.g. <domain>\<user>)"
$svcPassword = Read-Host -AsSecureString "Please enter the service account password"
$IPaddr = Read-Host "Please enter the streaming IP address"
$licServer = Read-Host "Please enter the license server name (leave blank to skip)"
 
# Function to Convert Securestring to Plaintext
Function ConvertTo-PlainText( [security.securestring]$secure ) {
$marshal = [Runtime.InteropServices.Marshal]
$marshal::PtrToStringAuto( $marshal::SecureStringToBSTR($secure) )
}
 
# Silently Install PVS Server
Write-Host "Installing Provisioning Services ... " -nonewline
$cmd=[System.Diagnostics.Process]::Start($installer_srv,'/s /v"/passive /l* c:\pvs_srv.log"')
$cmd.WaitForExit()
Write-Host "Done."
 
# Add Database Reg Keys
Write-Host "Adding Database Registry Keys ..."
reg add HKLM\Software\Citrix\ProvisioningServices\Database /v Database /d $dbName /f
reg add HKLM\Software\Citrix\ProvisioningServices\Database /v Instance /f #/d $dbInstance
reg add HKLM\Software\Citrix\ProvisioningServices\Database /v Server /d $dbServer /f
 
# Configure Service Account
$cmd=[System.Diagnostics.Process]::Start($ntrights,"+r SeServiceLogonRight -u "+$svcUser)
$cmd.WaitForExit()
$cmd=[System.Diagnostics.Process]::Start($subinacl,"/nostatistic /service streamservice /grant="+$svcUser+"=TOP")
$cmd.WaitForExit()
$rule=New-Object System.Security.AccessControl.RegistryAccessRule($svcUser,"FullControl","ContainerInherit","None","Allow")
$acl=get-acl HKLM:\SOFTWARE\Citrix\ProvisioningServices
$acl.SetAccessRule($rule)
set-acl HKLM:\SOFTWARE\Citrix\ProvisioningServices $acl
icacls "c:\ProgramData\Citrix\Provisioning Services" /grant ($svcUser+':(R,W)')
icacls "c:\ProgramData\Citrix\Provisioning Services" /grant ($svcUser+':(OI)(CI)(IO)(GR,GW)')
 
# Configure and Start Soap Service
netsh http add urlacl url=http://+:54321/pvs/mapi/commandset/ user=$svcUser
$svc = gwmi win32_service -filter "name='soapserver'"
$svc.change($null,$null,$null,$null,"Automatic",$null,$svcUser,(ConvertTo-PlainText($svcPassword))) > $null
net start soapserver
Write-Host "Waiting for Soap Server to Initialize ..."
start-sleep 10
 
# Create Server Object in Database
Write-Host "Adding Server to Farm ..."
$cmd=[System.Diagnostics.Process]::Start("C:\Program Files\Citrix\Provisioning Services Console\MCLI.exe","add server -r serverName="+(hostname)+" siteName="+$siteName+" ip="+$IPaddr)
$cmd.WaitForExit()
 
# Configure Farm and Server Objects
Write-Host "Configuring Farm and Server Objects ..."
$cmd=[System.Diagnostics.Process]::Start("C:\Program Files\Citrix\Provisioning Services Console\MCLI.exe","set farm -r auditingEnabled=1 offlineDatabaseSupportEnabled=1")
$cmd.WaitForExit()
if (!($licServer -like "")) {
   $cmd=[System.Diagnostics.Process]::Start("C:\Program Files\Citrix\Provisioning Services Console\MCLI.exe","set farm -r licenseServer="+$licServer)
   $cmd.WaitForExit()
}
$cmd=[System.Diagnostics.Process]::Start("C:\Program Files\Citrix\Provisioning Services Console\MCLI.exe","set server -p servername="+(hostname)+" -r adMaxPasswordAgeEnabled=1 eventLoggingEnabled=1")
$cmd.WaitForExit()
 
# Configure and Start Stream Service
reg add HKLM\System\CurrentControlSet\Services\Tcpip\Parameters /v DisableTaskOffload /t REG_DWORD /d 1 /f
reg add HKLM\Software\Citrix\ProvisioningServices\IPC /v PortBase /t REG_DWORD /d 6890 /f
reg add HKLM\Software\Citrix\ProvisioningServices\IPC /v PortCount /t REG_DWORD /d 20 /f
$svc = gwmi win32_service -filter "name='streamservice'"
$svc.change($null,$null,$null,$null,"Automatic",$null,$svcUser,(ConvertTo-PlainText($svcPassword))) > $null
net start streamservice
 
# Configure and Start Two Stage Boot Service
$svc = gwmi win32_service -filter "name='pvstsb'"
$svc.change($null,$null,$null,$null,"Automatic") > $null
net start pvstsb
 
# Create local store and grant service account permissions
md C:\VDISKS
icacls C:\VDISKS /grant ($svcUser+':(OI)(CI)(F)')
 
# Silently Install PVS Console
Write-Host "Installing Provisioning Services Console ... " -nonewline
$cmd=[System.Diagnostics.Process]::Start($installer_cons,'/s /v"/passive /l* c:\pvs_cons.log"')
$cmd.WaitForExit()
Write-Host "Done."

Unattended PVS farm joining

Now We know how to install a new Citrix Provisioning Services server and set it up. The next part of this blog is how to automate the addition of PVS Farm members with ConfigWizard.exe (located wiht a default installation in C:\Program Files\Citrix\Provisioning Services\)

First you need to go to the directory where ConfigWizard.exe is located on a configured PVS server (The one we just installed for ex) to create a response file ConfigWizard.ans, you can do it by running ConfigWizard.exe /s and follow the next steps :

You will find the ConfigWizard.and file in the following directory : “C:\ProgramData\Citrix\Provisioning Services” Here is my ConfigWizard.ans file :

PXEServiceType=1
FarmConfiguration=2
DatabaseServer=SUOMIAPP01,1433
DatabaseInstance=TEST_PVS
FarmExisting=ProvisioningServices
ExistingSite=Saint-Germain-en-Laye
ExistingStore=Store
UserName=
UserName2=
Network=1
Database=1
PasswordManagementInterval=7
StreamNetworkAdapterIP=xxx.xxx.xxx.xxx
IpcPortBase=6890
IpcPortCount=20
SoapPort=54321
BootstrapFile=C:\ProgramData\Citrix\Provisioning Services\Tftpboot\ARDBP32.BIN
LS1=192.168.0.103,0.0.0.0,0.0.0.0,6910

Now to use this answer file, we need to deploy a new PVS server, copy the ConfigWizard.ans file onto the new servers within the farm and modify the IP address in the ConfigWizard.ans file to match each server in the PVS farm. Once the file is copied in the same directory as ConfigWizard.exe you just need to run the following command : ConfigWizard.exe /a , reboot your server and you’re done !

To automate this last step, you need to modify the ConfigWizard.ans file by changing the StreamNetworkAdapterIP address to xxx.xxx.xxx.xxx , I will this string in the next script to search and replace by the new PVS server :

# Pre-reqs:
# PVS Server installed
 
# Constants
$pvs_dir = "C:\Program Files\Citrix\Provisioning Services\"
$ans_file = "C:\PVS_auto_install\ConfigWizard.ans"
$confwiz = "C:\Program Files\Citrix\Provisioning Services\ConfigWizard.exe"
$ipaddr = ((ipconfig | findstr [0-9].\.)[0]).Split()[-1]
 
# Replace in file
Get-Content $ans_file | ForEach-Object { $_ -replace "xxx.xxx.xxx.xxx", $ipaddr } | Set-Content ($ans_file+".tmp")
Remove-Item $ans_file
Rename-Item ($ans_file+".tmp") $ans_file
 
# Silently Install PVS Console
Write-Host "Configuring PVS Server ... " -nonewline
Copy-Item $ans_file $pvs_dir
$cmd=[System.Diagnostics.Process]::Start($confwiz,'/a')
$cmd.WaitForExit()
Write-Host "Done."

That’s it, you have everything now auto deploy as many Provisioning Server as you want !

Sources :
Citrix eDocs
PVS Fully Automated Install – Citrix Community
Microsoft Resource Kit

Post author