I’ve been spending a lot of time lately thinking about how organizations can provide services can operationalize the principles behind Infrastructure as Code in a way that’s accessible. In my experience, if a tool isn’t easy to use, it won’t be used. This is one of the biggest barriers to organizations (and companies that provide services, like mine) benefiting from IaC. It’s my intention that this script will achieve a high level of usability and will also generate and consume reusable code for future use.
Table of Contents
Recap
Challenge
You want to use Infrastructure as Code principles to deploy your vCenter Server, but you want to ensure that your solution is accessible without code as well.
Solution
Create a script that gathers all relevant information for a VCSA deployment via the the vcsa-deploy and generates the JSON file that the utility consumes, while also allowing for the download of the VCSA ISO from a web server.
Background
In order for this to fit my definition of infrastructure as code, we should be able to define all details of the vCenter in a structured format. Additionally, we should not assume that the vCenter installer is local, and should pull it from an external location if we need it to. While the vcsa-deploy utility can handle the installation of the vCenter, I’ve found most people don’t like it.
With this script, I allow a VI admin to fill out all relevant details of the vCenter in a spreadsheet. The script will then access the spreadsheet directly, and generate the JSON file used by vcsa-deploy. As a bonus, it will call the vcsa-deploy utility and kick off the actual deployment.
Demo
Features
- Validate Password Complexity
- Provide Input via Excel Spreadsheet or JSON file
- Download ISO, extract contents
- Deploy vCenter to either ESXi or vCenter
Requirements
- ImportExcel module
- 7-zip
- URL to a vCenter Server 6.7 Appliance ISO
- Standard vCenter Prerequisites (forward/reverse DNS, space, networking, etc) (link)
Code Review
The code can be located on my github account. As per usual, let’s go through the code.
Lines 1-31
Documentation
Lines 32-58
Define parameters
Lines 66-91
Download and install the ISO
Lines 93-130
Extract the ISO using 7-zip
Lines 133-139
Use import-excel to grab sections of the spreadsheet. There’s a bit of overhead, but my goal here was for the spreadsheet to look good.
Lines 141-152
Validate the password specified meets VMware’s requirements for the VCSA
Lines 154-172
Detect the OS
Lines 176-224
Determine whether we are deploying to a vCenter or an ESXi host and create the respective JSON. This is the only difference between deploying to a vCenter or an ESXi host.
Lines 226-298
Create a hashtable used for to create the rest of parameters common to a the json used by vcsa-deploy.
Lines 300-312
Write the JSON file to disk
Lines 314-333
Begin the install
Lines 335-343
Determine if the install was successful. If it failed, print out the error messages.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
|
#Github: https://github.com/jonhowe/Virtjunkie.com/tree/master/VCSA-AutoDeploy
#Blog Post: http://www.virtjunkie.com/deploy-vcsa-via-powershell/
<#
.SYNOPSIS
Install the VMware vCenter Server Appliance based on input from a Microsoft Excel File
.DESCRIPTION
This script will install the VMware vCenter Server Appliance based on input from a Microsoft Excel File.
.PARAMETER ForceDownload
Switch parameter that will force a (re) download and extract of the ISO
.PARAMETER URI
This is the URL of the vCenter Installer ISO
.PARAMETER Workspace
Absolute path to the directory that will be used as a workspace. The vCenter ISO will be downloaded there, and will be
extracted there as well.
.PARAMETER DeploymentTarget
Location that the new VCSA will be deployed to.
Valid options are: ESXi, vCenter
.PARAMETER ExcelFile
Absolute path to the Excel file that contains the parameters required for vCenter to be built
.PARAMETER 7z
Absolute path path to the 7z binary
#>
#Requires -modules Microsoft.powershell.archive,importexcel
[CmdletBinding()]
param (
[switch]$ForceDownload
,
$URI = "http://jondesktop.home.lan:8123/files/VMware/VSMRepo/dlg_VC67U3B/VMware-VCSA-all-6.7.0-15132721.iso"
,
[Parameter(Mandatory=$true)]
[ValidateScript( {
if ( -Not ($_ | Test-Path) ) {
throw "File or folder does not exist"
}
return $true
})]
$Workspace
,
[validateset("vCenter", "ESXi")]
$deploymentTarget
,
[ValidateScript( { Test-Path $_ })]
[string]
$ExcelFile = "/home/jhowe/git/Personal/AutomatedLab/JonAutoLab/HostDetails.xlsx"
,
[Parameter(Mandatory=$true)]
[ValidateScript( { Test-Path $_ })]
[string]
$7z = '/usr/bin/7z'
)
$ErrorActionPreference = 'Stop'
#region Download and Extract the ISO
$VCSA_Installer_Archive = $workspace + ($uri.Split('/')[-1])
$VCSA_Extracted_Directory = ($VCSA_Installer_Archive.Split(".iso")[0])
$VCSA_CLI_Installer_Path = ($VCSA_Extracted_Directory + "/vcsa-cli-installer/")
#Download the VCSA only if it doesn't already exist
if ((Test-Path $VCSA_Installer_Archive) -eq $false -or $forcedownload) {
$filesize = (Invoke-WebRequest $uri -Method Head).Headers.'Content-Length'
[int]$WebServerFileSize = ([convert]::ToInt64($filesize, 10)) / 1024
Write-Output "Downloading VCSA Installer"
$wc = New-Object System.Net.WebClient
$wc.DownloadFileAsync($uri, $VCSA_Installer_Archive)
#The following loop will print the progressof the download every 15 seconds
$incomplete = $true
while ($incomplete) {
$CurrentSize = ((Get-Item $VCSA_Installer_Archive).length) / 1024
if ($CurrentSize -eq $WebServerFileSize) {
$incomplete = $false
}
else {
Write-Output "File Download Size is $([math]::round($CurrentSize,0)) / $($WebServerFileSize) ($(($CurrentSize / $WebServerFileSize).ToString("P") )%)"
Start-Sleep -Seconds 15
}
}
}
else {
Write-Output "VCSA Installer already exists"
}
#Extract the VCSA only if the directory it should be extracted to doesn't already exist
if ((Test-Path ($VCSA_Extracted_Directory)) -eq $false -or $forcedownload) {
Write-Output "Unzipping VCSA"
#Expand-Archive -LiteralPath $VCSA_Installer_Archive -destinationpath $workspace
Write-Output "Extracting the ISO now..."
$arguments = "x -bb0 -bd -y -o$($VCSA_Extracted_Directory) $($VCSA_Installer_Archive)"
Write-Verbose -Message ($7z + " " + $arguments)
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $7z
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $arguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$7z_stdout = $p.StandardOutput.ReadToEnd()
$7z_stderr = $p.StandardError.ReadToEnd()
$7z_stdout | out-file -Path ($workspace + "7z-stdout.txt")
$7z_stderr | out-file -Path ($workspace + "7z-stderr.txt")
if ($p.ExitCode -ne 0) {
Write-Output "There was an error..."
Write-Output "Process finished with exit code $($p.ExitCode)"
Write-Output "Log data written to: $($workspace)7z-stdout.txt and $($workspace)7z-stderr.txt"
$7z_stderr
}
else {
Write-Output "Extraction completed successfully."
}
}
else {
Write-Output "VCSA has already been unzipped"
}
#endregion Download and Extract the ISO
#region import excel file into sections and validate input
$targettype = Import-Excel $excelfile -WorksheetName vCenter -StartRow 1 -EndRow 2 -StartColumn 1 -EndColumn 1
$targetinfo = Import-Excel $excelfile -WorksheetName vCenter -StartRow 1 -EndRow 2 -StartColumn 2 -EndColumn 8
$applianceinfo = Import-Excel $excelfile -WorksheetName vCenter -StartRow 4 -EndRow 5 -StartColumn 1 -EndColumn 3
$networkinfo = Import-Excel $excelfile -WorksheetName vCenter -StartRow 7 -EndRow 8 -StartColumn 1 -EndColumn 7
$os = Import-Excel $excelfile -WorksheetName vCenter -StartRow 10 -EndRow 11 -StartColumn 1 -EndColumn 3
$sso_ciep = Import-Excel $excelfile -WorksheetName vCenter -StartRow 13 -EndRow 14 -StartColumn 1 -EndColumn 3
#Ensure the password specified has the appropriate complexity based on vCenter's requirements
$input = ($os."vCenter Root Password")
if (($input -cmatch '[a-z]') `
-and ($input -match '\d') `
-and ($input.length -ge 8) `
-and ($input -match '!|@|#|%|^|&|$')) {
Write-Output "Password complexity is sufficient. Continuing."
}
else {
Write-Output "$input does not meet complexity requirements. Password must be at least 8 characters, include one numeric, and one special character."
exit
}
$deploymentTarget = $targettype.'vCenter or ESXi'
#endregion import excel file into sections and validate input
#region Detect the OS (Windows, Linux, OSX)
if ($IsWindows -or $ENV:OS) {
$installer_dir = ($VCSA_CLI_Installer_Path + "win32/")
$installer = ($installer_dir + "vcsa-deploy.exe")
}
elseif ($IsLinux) {
$installer_dir = ($VCSA_CLI_Installer_Path + "lin64/")
$installer = ($installer_dir + "vcsa-deploy")
chmod -R +x $workspace
}
elseif ($IsMacOS) {
$installer_dir = ($VCSA_CLI_Installer_Path + "mac/")
$installer = ($installer_dir + "vcsa-deploy")
}
#endregion Detect the OS (Windows, Linux, OSX)
#region Create and format the JSON
#Detect whether we are deploying to vCenter or ESXi
switch ($deploymentTarget) {
'vCenter' {
Write-Output "Deploying to vCenter"
$target = [ordered] @{
vc = [ordered]@{
__comments = @("'datacenter' must end with a datacenter name, and only with a datacenter name. ",
"'target' must end with an ESXi hostname, a cluster name, or a resource pool name. ",
"The item 'Resources' must precede the resource pool name. ",
"All names are case-sensitive. ",
"For details and examples, refer to template help, i.e. vcsa-deploy {install|upgrade|migrate} --template-help")
hostname = $targetinfo."Target Host or vCenter"
username = $targetinfo."Username"
password = $targetinfo."Password"
deployment_network = $targetinfo."PortGroup"
<#
datacenter = @("Folder 1 (parent of Folder 2)",
"Folder 2 (parent of Your Datacenter)",
"Your Datacenter")
#>
datacenter = $targetinfo."Datacenter (vCenter Only)"
datastore = $targetinfo."Datastore"
<#
target = @( "Folder A (parent of Folder B)",
"Folder B (parent of Your ESXi Host, or Cluster)",
"Your ESXi Host, or Cluster")
#>
target = $targetinfo."Cluster Name (vCenter Only)"
}
}
}
'ESXi' {
Write-Output "Deploying to ESXi"
$target = [ordered] @{
esxi = [ordered]@{
hostname = $targetinfo."Target Host or vCenter"
username = $targetinfo."Username"
password = $targetinfo."Password"
deployment_network = $targetinfo."PortGroup"
datastore = $targetinfo."Datastore"
}
}
}
Default {
Write-Error "Invalid Deployment Target Specified. Exiting."
exit
}
}
<#
I'm aware that others have imported the json file provided by the installer...
I don't have a great reason that I didn't do that.. maybe I'm just different :-)
Either way - we are simply generating a hashtable here, and then writing it to a file.
#>
$common_properties = @{
appliance = [ordered]@{
__comments = @("You must provide the 'deployment_option' key with a value, which will affect the VCSA's configuration parameters, such as the VCSA's number of vCPUs, the memory size, the storage size, and the maximum numbers of ESXi hosts and VMs which can be managed. For a list of acceptable values, run the supported deployment sizes help, i.e. vcsa-deploy --supported-deployment-sizes")
thin_disk_mode = $applianceinfo."Thin Disk?"
deployment_option = $applianceinfo."vCenter Size"
name = $applianceinfo."VM Name"
}
network = [ordered]@{
ip_family = $networkinfo."IP v4 or v6"
mode = $networkinfo."IP Allocation Mode"
ip = $networkinfo."IP Address"
dns_servers = @($networkinfo.'DNS Server List').split(",")
prefix = ($networkinfo."CIDR Block").tostring()
gateway = $networkinfo."Default Gateway"
system_name = $networkinfo."vCenter Hostname"
}
os = [ordered]@{
password = $os."vCenter Root Password"
ntp_servers = $os."NTP Servers"
ssh_enable = $os."Enable SSH By Default?"
}
sso = [ordered]@{
password = $sso_ciep."SSO Password"
domain_name = $sso_ciep."SSO Domain Name"
}
}
$combined = [ordered]@{
__version = "2.13.0"
__comments = "Sample template to deploy a vCenter Server Appliance with an embedded Platform Services Controller on an ESXi host."
new_vcsa = $target + $common_properties
ceip = [ordered]@{
description = @{
__comments = @("++++VMware Customer Experience Improvement Program (CEIP)++++",
"VMware's Customer Experience Improvement Program (CEIP) ",
"provides VMware with information that enables VMware to ",
"improve its products and services, to fix problems, ",
"and to advise you on how best to deploy and use our ",
"products. As part of CEIP, VMware collects technical ",
"information about your organization's use of VMware ",
"products and services on a regular basis in association ",
"with your organization's VMware license key(s). This ",
"information does not personally identify any individual. ",
"",
"Additional information regarding the data collected ",
"through CEIP and the purposes for which it is used by ",
"VMware is set forth in the Trust & Assurance Center at ",
"http://www.vmware.com/trustvmware/ceip.html . If you ",
"prefer not to participate in VMware's CEIP for this ",
"product, you should disable CEIP by setting ",
"'ceip_enabled': false. You may join or leave VMware's ",
"CEIP for this product at any time. Please confirm your ",
"acknowledgement by passing in the parameter ",
"--acknowledge-ceip in the command line.",
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
}
settings = @{
#TODO I believe an extra member is needed if this is enabled
ceip_enabled = $sso_ciep."Enable CIEP?"
}
}
}
$json = $combined | ConvertTo-Json -Depth 99
$fixtrue = $json.replace("`"true`"", "true")
$FinalJSON = $fixtrue.replace("`"false`"", "false")
#endregion Create and format the JSON
#region Write the json file to disk
switch ($deploymentTarget) {
'vCenter' {
$FinalConfigFile = $workspace + "$(($FinalJSON | convertfrom-json).new_vcsa.vc.hostname).json"
}
'ESXi' {
$FinalConfigFile = $workspace + "$(($FinalJSON | convertfrom-json).new_vcsa.esxi.hostname).json"
}
Default { }
}
$FinalJSON | out-file -FilePath $FinalConfigFile
Write-Output ("JSON file written to: " + $FinalConfigFile)
#endregion Write the json file to disk
#region Start the install
Write-Output "Beginning install now.. this will take a few minutes. Please wait."
$arguments = "install --accept-eula --no-ssl-certificate-verification --log-dir $($VCSA_CLI_Installer_Path) $($FinalConfigFile)"
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $installer
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $arguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$stdout | out-file -Path ($VCSA_CLI_Installer_Path + "stdout.txt")
$stderr | out-file -Path ($VCSA_CLI_Installer_Path + "stderr.txt")
#endregion Start the install
if ($p.ExitCode -ne 0) {
Write-Output "There was an error..."
Write-Output "Process finished with exit code $($p.ExitCode)"
Write-Output "Log data written to: $($VCSA_CLI_Installer_Path)stdout.txt and $($VCSA_CLI_Installer_Path)stderr.txt"
$stderr
}
else {
Write-Output "Install completed successfully."
}
|
What’s next?
Good question! vSphere 7 is releasing vCenter Server Profiles and Image Cluster Management. Creating tools that can consume these new features will be helpful.
I’m working on template management using Hashicorp’s Packer and will be posting some of my work on that soon. In addition, I have done some work on Hashicorp’s Terraform for deploying lab VMs that I’ll share too. Finally – I’ve got a few skeleton Ansible Playbooks that I’m working on for configuration of the lab VMs, as well as a playbook for doing high level configuration of a bare vCenter. So in short – lots more coming!
Thanks for viewing,
Jon
Interesting. There are few points which I would like to highlight.
1. deploying VCSA using this method where there is no internet to download.
2. Instead internet download, how about specifying alternative intranet locations such as FTP / NAS?
3. The script developed based on linux environment, Windows version would much helpful
Hey @KKrishna, thanks for stopping by.
Re: #3 – You’re spot on.. I did use Linux to develop this, but it doesn’t make a difference. Just specify Windows paths to the 7-zip binary and your excel file and the rest should be fine.
For #1, I actually designed this to run without internet access. An example for how to do this is here: https://www.virtjunkie.com/poor-mans-autodeploy/#Web_Server
For #2, love the idea of pulling via FTP. I’d welcome a PR here: https://github.com/jonhowe/Virtjunkie.com/tree/master/VCSA-AutoDeploy
Thanks for your prompt response.
Unfortunately. I’m new to PS and as well Git. But I liked your approach. For me, I’m looking for script that start from place where ISO already extracted. So of now I’m trying to tailor your script.
Hi – Great script! Nonetheless I’m pondering a bit why you don’t only extract the ova from the ISOvcsa and deploy using “ovf-tool”. Do you have any specific reason you could share?
Good question Cristoph! I typically use ovftool when I deploy vCenter using ansible, but in this case, I wanted to use the tool that VMware provided, vcsa-deploy.