Public/Install-VCDAAVS.ps1

<#
Copyright 2023 VMware, Inc.
SPDX-License-Identifier: BSD-2-Clause
#>


function Install-VCDAAVS {
    <#
.SYNOPSIS
   Install and configure VMware Cloud Director Availability instance in AVS
.DESCRIPTION
    Install and configure VMware Cloud Director Availability instance in AVS.
    Before running the install AVS site must be prepared by running 'Initialize-AVSSite' command.
    This command will Install a VCDA instance of a Manager, Tunnel and 2 Replicator appliances.
    You must Accept the End User License Agreement: "https://raw.githubusercontent.com/vmware/vmware-powercli-for-vmware-cloud-director-availability/main/Resources/EULA.txt"')]

.PARAMETER License
    Valid License key for VMware Cloud Director Availability
.PARAMETER SiteName
    Name of the VCDA site, cannot be changed after installation. Only latin alphanumerical characters and "-" are allowed in site name
.PARAMETER PublicApiEndpoint
    VCDA Public Service Endpoint address, "https://VCDA-FQDN:443"
.PARAMETER VCDApiEndpoint
    VMware Cloud Director Service endpoint URL, "https://VMware-Cloud-Director-IP-Address:443/api"
.PARAMETER VCDUser
    Username of a System administrator user in VMware Cloud Director Service. For example, use administrator@system'
.PARAMETER VCDPassword
    Password of the VMware Cloud Director Service System administrator user
.PARAMETER Datastore
    Name of the Datastore to be used for deployment of the appliances. The ova file must be available in the same Datastore
.PARAMETER Cluster
    Name of the Destination vSphere Cluster to be used for deployment of the appliances
.PARAMETER ManagerIPAddress
    IPv4 address in CIDR notation (for example 192.168.0.222/24) to be used for deployment of the Manager appliance
.PARAMETER ManagerGW
    Gateway IP address for the Manager Appliance
.PARAMETER ManagerHostname
    Hostname of the Manager appliance
.PARAMETER ManagerNetwork
    Name of the vSphere network to be used for deployment of the Manager appliance
.PARAMETER TunnelIPAddress
    IPv4 address in CIDR notation (for example 192.168.0.223/24) to be used for deployment of the Tunnel appliance.
.PARAMETER TunnelGW
    Gateway IP address for the Tunnel Appliance
.PARAMETER TunnelHostname
    Hostname of the Tunnel appliance
.PARAMETER TunnelNetwork
    Name of the vSphere network to be used for deployment of the Tunnel appliance
.PARAMETER Replicator1IPAddress
    IPv4 address in CIDR notation (for example 192.168.0.224/24) to be used for deployment of the First (1st) Replicator appliance
.PARAMETER Replicator1Hostname
    Hostname of the First (1st) Replicator appliance
.PARAMETER Replicator2IPAddress
    IPv4 address in CIDR notation (for example 192.168.0.225/24) to be used for deployment of the Second (2nd) Replicator appliance
.PARAMETER Replicator2Hostname
    Hostname of the Second (2nd) Replicator appliance
.PARAMETER ReplicatorGW
    Gateway IP address for 1st and 2nd Replicator appliances
.PARAMETER ReplicatorNetwork
    Name of the vSphere network to be used for deployment of 1st and 2nd Replicator appliances
.PARAMETER NTPServer
    NTP Server address to be used for all VCDA appliances
.PARAMETER DNSServer
    List of DNS server to be used for all appliances (for example, "192.168.1.1,192.168.1.2"
.PARAMETER SearchDomain
    List of search domain for all appliances (for example: "domain1.local,domain2.local")
.PARAMETER OVAFilename
    Name of the VCDA .ova file, located in a folder of the same Datastore where appliances will be deployed (for example: "VCDA-4.7.ova")
    Only 4.7.3 or 4.7.3.1 are supported versions.
.PARAMETER AcceptEULA
    Accept the End User License Agreement: "https://raw.githubusercontent.com/vmware/vmware-powercli-for-vmware-cloud-director-availability/main/Resources/EULA.txt"'
.EXAMPLE
    $params = @{
        'License' = 'XXXX-XXXX-XXXX-XXXX-XXXX' | ConvertTo-SecureString -AsPlainText -Force
        'SiteName' = 'vcda-demo'
        'PublicApiEndpoint' = 'https://VCDA-FQDN:443'
        'VCDApiEndpoint' = "https://VMware-Cloud-Director-IP-Address:443/api"
        'VCDUser' = 'administrator@system'
        'VCDPassword' = Read-Host -Prompt "Enter VCDPassword" -AsSecureString
        'Datastore' = 'Datastore01'
        'Cluster' = 'Cluster01'
        'ManagerIPAddress' = '192.168.0.222/24'
        'ManagerGW' = '192.168.0.1'
        'ManagerHostname' = 'vcda-m01'
        'ManagerNetwork' = 'vcda-network'
        'TunnelIPAddress' = '192.168.0.223/24'
        'TunnelGW' = '192.168.0.1'
        'TunnelHostname' = 'vcda-t01'
        'TunnelNetwork' = 'vcda-network'
        'Replicator1IPAddress' = '192.168.0.224/24'
        'Replicator1Hostname' = 'vcda-r01'
        'Replicator2IPAddress' = '192.168.0.225/24'
        'Replicator2Hostname' = 'vcda-r02'
        'ReplicatorGW' = '192.168.0.1'
        'ReplicatorNetwork' = 'vcda-network'
        'NTPServer' = 'time.vcda.local'
        'DNSServer' = '192.168.0.10,192.168.0.11'
        'SearchDomain' = 'vcda.local'
        'OVAFilename' = 'VMware-Cloud-Director-Availability-Provider-4.6.1.7681624-a5359f8567_OVF10.ova'
        'AcceptEULA' = $true
    }
    Install-VCDAAVS @params
#>

    [AVSAttribute(30, UpdatesSDDC = $false)]
    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Valid License key for VMware Cloud Director Availability')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ ($_ | ConvertFrom-SecureString -AsPlainText) -match '^\S{5}-\S{5}-\S{5}-\S{5}-\S{5}\b$' }, `
                ErrorMessage = "The provided License is not in a valid format. Expected format is: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX")]
        [SecureString]
        $License,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Name of the VCDA site, cannot be changed after installation. Only latin alphanumerical characters and "-" are allowed in site name.')]
        [ValidatePattern('^[a-zA-Z0-9-]+$', ErrorMessage = "'{0}' is not a valid Site name, Only latin alphanumerical characters and '-' are allowed" )]
        [string]
        $SiteName,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'VCDA Public Service Endpoint address, "https://VCDA-FQDN:443"')]
        [ValidateScript({ [system.uri]::IsWellFormedUriString($_, 'Absolute') -and ([system.uri]$_).Scheme -eq 'https' }, `
                ErrorMessage = "'{0}' is not a valid Public Endpoint address, it must be in the format 'https://VCDA-FQDN:443'")]
        [string]
        $PublicApiEndpoint,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'VMware Cloud Director Service endpoint URL, "https://VMware-Cloud-Director-IP-Address:443/api"')]
        [ValidateScript({ [system.uri]::IsWellFormedUriString($_, 'Absolute') -and ([system.uri]$_).Scheme -eq 'https' }, `
                ErrorMessage = "'{0}' is not a valid Public Endpoint address, it must be in the format 'https://VMware-Cloud-Director-IP-Address:443/api'")]
        [string]
        $VCDApiEndpoint,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Enter the user name of a System administrator user in VMware Cloud Director. For example, use administrator@system')]
        [ValidateNotNullOrEmpty()]
        [string]
        $VCDUser,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Enter the password of the VMware Cloud Director System administrator user')]
        [ValidateNotNullOrEmpty()]
        [SecureString]
        $VCDPassword,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Name of the Datastore to be used for deployment of the appliances')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Datastore,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Name of the Destination vSphere Cluster to be used for deployment of the appliances')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Cluster,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'IPv4 address in CIDR notation (for example 192.168.0.222/24) to be used for deployment of the Manager appliance')]
        [ValidateScript({
                [System.Net.IPAddress]($_.split("/")[0]) -and 0..32 -contains $_.Split("/")[1]
            }, ErrorMessage = "'{0}' is not valid format, IPv4 address in CIDR notation (for example 192.168.0.222/24)"
        )]
        [string]
        $ManagerIPAddress,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Gateway IP address for the Manager Appliance')]
        [ValidateScript({ [System.Net.IPAddress]($_) })]
        [string]
        $ManagerGW,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Hostname of the Manager appliance')]
        [string]
        $ManagerHostname,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'vSphere network to be used for deployment of the Manager appliance')]
        [ValidateNotNullOrEmpty()]
        [string]
        $ManagerNetwork,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'IPv4 address in CIDR notation (for example 192.168.0.223/24) to be used for deployment of the Tunnel appliance')]
        [ValidateScript({
                [System.Net.IPAddress]($_.split("/")[0]) -and 0..32 -contains $_.Split("/")[1]
            }, ErrorMessage = "'{0}' is not valid format, IPv4 address in CIDR notation (for example 192.168.0.223/24)"
        )]
        [string]
        $TunnelIPAddress,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Gateway IP address for the Tunnel Appliance')]
        [ValidateScript({ [System.Net.IPAddress]($_) })]
        [string]
        $TunnelGW,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Hostname of the Tunnel appliance')]
        [ValidateNotNullOrEmpty()]
        [string]
        $TunnelHostname,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'vSphere network to be used for deployment of the Tunnel appliance')]
        [ValidateNotNullOrEmpty()]
        [string]
        $TunnelNetwork,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'IPv4 address in CIDR notation (for example 192.168.0.224/24) to be used for deployment of the First (1st) Replicator appliance')]
        [ValidateScript({
                [System.Net.IPAddress]($_.split("/")[0]) -and 0..32 -contains $_.Split("/")[1]
            }, ErrorMessage = "'{0}' is not valid format, IPv4 address in CIDR notation (for example 192.168.0.224/24)"
        )]
        [string]
        $Replicator1IPAddress,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Hostname of the First (1st) Replicator appliance')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Replicator1Hostname,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'IPv4 address in CIDR notation (for example 192.168.0.225/24) to be used for deployment of the Second (2nd) Replicator appliance')]
        [ValidateScript({
                [System.Net.IPAddress]($_.split("/")[0]) -and 0..32 -contains $_.Split("/")[1]
            }, ErrorMessage = "'{0}' is not valid format, IPv4 address in CIDR notation (for example 192.168.0.225/24)"
        )]
        [string]
        $Replicator2IPAddress,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Hostname of the Second (2nd) Replicator appliance')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Replicator2Hostname,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Gateway IP address for 1st and 2nd Replicator appliances')]
        [ValidateScript({ [System.Net.IPAddress]($_) })]
        [string]
        $ReplicatorGW,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'vSphere network to be used for deployment of 1st and 2nd Replicator appliances')]
        [ValidateNotNullOrEmpty()]
        [string]
        $ReplicatorNetwork,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'NTP Server address to be used for all VCDA appliances')]
        [ValidateNotNullOrEmpty()]
        [string]
        $NTPServer,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'List of DNS server to be used for all appliances (for example, "192.168.1.1,192.168.1.2"')]
        [ValidateNotNullOrEmpty()]
        [string]
        $DNSServer,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'List of search domain for all appliances (for example: "domain1.local,domain2.local")')]
        [ValidateNotNullOrEmpty()]
        [string]
        $SearchDomain,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'Named of the VCDA .ova file, located in a folder of the same Datastore where appliances will be deployed (for example: "VCDA-4.7.ova")')]
        [ValidateNotNullOrEmpty()]
        [string]
        $OVAFilename,
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Accept the End User License Agreement: "https://raw.githubusercontent.com/vmware/vmware-powercli-for-vmware-cloud-director-availability/main/Resources/EULA.txt"')]
        [ValidateNotNullOrEmpty()]
        [switch]
        $AcceptEULA
    )

    try {
        #make sure vc connection is healthy, script will fail if not
        if ($null -eq ((Get-View SessionManager -Server $global:DefaultVIServer).CurrentSession)) {
            Write-Error "vCenter server '$($Global:defaultviserver.Name)' connection is not heathy."
        }
        if ($AcceptEULA -ne $true) {
            Write-Error 'You must accept the End User License Agreement "https://raw.githubusercontent.com/vmware/vmware-powercli-for-vmware-cloud-director-availability/main/Resources/EULA.txt" to install VCDA. '
        }
        #make sure SDDC is prepared
        Get-AVSSiteStatus -LogPrefix "[PRE-CHECK]"
        #get folder
        $vm_folder = Get-Folder -Name $Script:vcda_avs_params.vsphere.folder -Type VM -Location ([AVSSecureFolder]::root())
        $LocalVarCommonParams = @{
            'OVAFilename'       = $OVAFilename
            'NTP'               = $NTPServer
            'DNS'               = $DNSServer
            'Domains'           = $SearchDomain
            'Datastore'         = $Datastore
            'MTU'               = 1500
            'InventoryLocation' = $Script:vcda_avs_params.vsphere.folder
            'cluster'           = $cluster
        }

        $LocalVarCommonParams.DNS = $DNSServer
        $LocalVarCommonParams.Domains = $SearchDomain

        $LocalvarManagerParams = @{
            'IPAddress' = $ManagerIPAddress
            'Gateway'   = $ManagerGW
            'hostname'  = $ManagerHostname
        }
        $LocalvarTunnelParams = @{
            'IPAddress' = $TunnelIPAddress
            'Gateway'   = $TunnelGW
            'hostname'  = $TunnelHostname
        }
        $LocalvarRepl01Params = @{
            'IPAddress' = $Replicator1IPAddress
            'Gateway'   = $ReplicatorGW
            'hostname'  = $Replicator1Hostname
        }
        $LocalvarRepl02Params = @{
            'IPAddress' = $Replicator2IPAddress
            'Gateway'   = $ReplicatorGW
            'hostname'  = $Replicator2Hostname
        }

        #1 deploy manager
        $man_vm_name = 'VCDA-AVS-Manager-01'
        Write-Log -message "Starting Manager installation, VM name is '$man_vm_name'." -LogPrefix "[DEPLOY-MANAGER]"
        $man_pass = Get-VCDAVMPassword -name $man_vm_name
        $manager_vm = Deploy-VCDAOVA @LocalVarCommonParams @LocalvarManagerParams -DeploymentOption cloud -Name $man_vm_name `
            -network $ManagerNetwork -password $man_pass.old -LogPrefix "[DEPLOY-MANAGER]"
        [avSSecureFolder]::Secure($vm_folder)
        #$manager_vm | Add-VCDATag

        #2 deploy tunnel
        $tun_vm_name = 'VCDA-AVS-Tunnel-01'
        Write-Log -message "Starting Tunnel installation, VM name is '$tun_vm_name'." -LogPrefix "[DEPLOY-TUNNEL]"
        $tun_pass = Get-VCDAVMPassword -name $tun_vm_name
        $tunnel_vm = Deploy-VCDAOVA @LocalVarCommonParams @LocalvarTunnelParams -DeploymentOption tunnel -Name $tun_vm_name `
            -network $TunnelNetwork -password $tun_pass.old -LogPrefix "[DEPLOY-TUNNEL]"
        [avSSecureFolder]::Secure($vm_folder)
        #$tunnel_vm | Add-VCDATag

        #3 deploy replicator 1
        $repl1_vm_name = 'VCDA-AVS-Replicator-01'
        Write-Log -message "Starting 1st replicator installation, VM name is '$repl1_vm_name'." -LogPrefix "[DEPLOY-REPLICATOR-1]"
        $repl1_pass = Get-VCDAVMPassword -name $repl1_vm_name
        $repl1_vm = Deploy-VCDAOVA @LocalVarCommonParams @LocalvarRepl01Params -DeploymentOption replicator -Name $repl1_vm_name `
            -network $ReplicatorNetwork -password $repl1_pass.old -LogPrefix "[DEPLOY-REPLICATOR-1]"
        [avSSecureFolder]::Secure($vm_folder)
        #$repl1_vm | Add-VCDATag

        #4 deploy replicator 2
        $repl2_vm_name = 'VCDA-AVS-Replicator-02'
        Write-Log -message "Starting 2nd replicator installation, VM name is '$repl2_vm_name'." -LogPrefix "[DEPLOY-REPLICATOR-2]"
        $repl2_pass = Get-VCDAVMPassword -name $repl2_vm_name
        $repl2_vm = Deploy-VCDAOVA @LocalVarCommonParams @LocalvarRepl02Params -DeploymentOption replicator -Name $repl2_vm_name `
            -network $ReplicatorNetwork -password $repl2_pass.old -LogPrefix "[DEPLOY-REPLICATOR-2]"
        [avSSecureFolder]::Secure($vm_folder)
        #$repl2_vm | Add-VCDATag
        Write-Log -message "All VCDA VMs deployed successfully." -LogPrefix "[DEPLOY-COMPLETED]"

        Write-Log -message "Starting Manager configuration, VM name is '$man_vm_name'." -LogPrefix "[CONFIG-MANAGER]"
        Initialize-VCDAAppliance -VCDA_VM $manager_vm -LicenseKey ($license | ConvertFrom-SecureString -AsPlainText) `
            -SiteName $SiteName -PublicApiEndpoint $PublicApiEndpoint -vcd_api_endpoint $VCDApiEndpoint `
            -vcd_user $VCDUser -vcd_password $VCDPassword -IPAddress $LocalvarManagerParams.IPAddress -LogPrefix "[CONFIG-MANAGER]"

        Initialize-VCDAAppliance -VCDA_VM $tunnel_vm -IPAddress $LocalvarTunnelParams.IPAddress -LogPrefix "[CONFIG-TUNNEL]"

        Initialize-VCDAAppliance -VCDA_VM $repl1_vm -IPAddress $LocalvarRepl01Params.IPAddress -LogPrefix "[CONFIG-REPLICATOR-1]"
        Initialize-VCDAAppliance -VCDA_VM $repl2_vm -IPAddress $LocalvarRepl02Params.IPAddress -LogPrefix "[CONFIG-REPLICATOR-2]"
        Write-Log -message "All VCDA Appliances Configured Successfully." -LogPrefix "[CONFIG-COMPLETED]"

        Register-Replicators -VCDA_Manager_VM $manager_vm -VCDA_replicator_VM $repl1_vm -LogPrefix "[REGISTER-REPLICATOR-1]"
        Register-Replicators -VCDA_Manager_VM $manager_vm -VCDA_replicator_VM $repl2_vm -LogPrefix "[REGISTER-REPLICATOR-2]"

        Register-Tunnel -VCDA_Manager_VM $manager_vm -VCDA_tunnel_vm $tunnel_vm -LogPrefix "[REGISTER-TUNNEL]"

        $vms_details = @()
        $vms_details += "" | Select-Object @{N = "VM Name"; E = { $man_vm_name } }, @{N = "Service"; E = { "CLOUD" } }, `
        @{N = "Address"; E = { "https://" + $($manager_vm.ExtensionData.guest.IpAddress) + "/ui/admin" } }
        $vms_details += "" | Select-Object @{N = "VM Name"; E = { $man_vm_name } }, @{N = "Service"; E = { "MANAGER" } }, `
        @{N = "Address"; E = { "https://" + $($manager_vm.ExtensionData.guest.IpAddress) + ":8441/ui/admin" } }
        $vms_details += "" | Select-Object @{N = "VM Name"; E = { $tun_vm_name } }, @{N = "Service"; E = { "TUNNEL" } }, `
        @{N = "Address"; E = { "https://" + $($tunnel_vm.ExtensionData.guest.IpAddress) + "/ui/admin" } }
        $vms_details += "" | Select-Object @{N = "VM Name"; E = { $repl1_vm_name } }, @{N = "Service"; E = { "REPLICATOR" } }, `
        @{N = "Address"; E = { "https://" + $($repl1_vm.ExtensionData.guest.IpAddress) + "/ui/admin" } }
        $vms_details += "" | Select-Object @{N = "VM Name"; E = { $repl2_vm_name } }, @{N = "Service"; E = { "REPLICATOR" } }, `
        @{N = "Address"; E = { "https://" + $($repl2_vm.ExtensionData.guest.IpAddress) + "/ui/admin" } }

        Write-Host `n "To access VMware Cloud Director Availability Public Endpoint use Cloud Director credentials.
        Address: '$PublicApiEndpoint'"


        Write-Log -message "Installation of VMware Cloud Director Availability completed successfully."

        Write-Host `n "To access the VCDA appliances admin UI use SSO authentication with 'cloudadmin' credentials."
        Write-Host ($vms_details | Format-Table -AutoSize -Wrap | Out-String)

        Write-Output "Installation of VMware Cloud Director Availability completed successfully."
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}