ZertoAVSModule.psm1

using module Microsoft.AVS.Management


$ZERTO_FOLDER_ON_HOST = "/var/zerto"
$LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS = ('{0}/zertoDriverLogs/' -f $psScriptRoot)
$LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES = ('{0}/filesFromDatastore/' -f $psScriptRoot)
$ZERTO_USER_NAME = "ZertoDR"
$ZERTO_ROLE = "ZertoRole"
$DOMAIN = "vsphere.local"


Function IsZertoUserExists
{
    <#
        .DESCRIPTION
        Get a zertoUsername and a domain, and return whether or not the user exists in the domain.
             
        .EXAMPLE
 
        IsZertoUserExists
    #>

        
    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        if(Get-SsoPersonUser -Name $ZERTO_USER_NAME -Domain $DOMAIN -ErrorAction SilentlyContinue) {
            Write-Host "$ZERTO_USER_NAME already exists in $VC_ADDRESS, domain: $DOMAIN."
            return $true;
        }
        
        Write-Host "$ZERTO_USER_NAME doesn't exist in $VC_ADDRESS, domain: $DOMAIN."
        return $false;
    }
}

Function IsZertoRoleExists
{
    <#
        .DESCRIPTION
        Return true if ZertoRole exists, otherwise return false.
             
        .EXAMPLE
 
        IsZertoRoleExists
    #>


    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        If (Get-VIRole -Name $ZERTO_ROLE -ErrorAction SilentlyContinue) {
            Write-Host "$ZERTO_ROLE already exists in $VC_ADDRESS"
            return $true
        } 
        
        Write-Host "$ZERTO_ROLE doesn't exist in $VC_ADDRESS"
        return $false;
    }
}

Function IsZertoDriverLoaded
{
    <#
        .DESCRIPTION
        Return true if Zerto driver is loaded, otherwise return false.
         
            .PARAMETER HostName
            Host Name to connect with SSH
 
        .EXAMPLE
 
        IsZertoDriverLoaded -HostName <HostName>
    #>

    
    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName 
    
    )
    
    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $vmkload = '/tmp/vmklod_output'
        $ZertoDriver = '/tmp/zertoDriver'
        
        $Command =    ('vmkload_mod -l > {0}' -f $vmkload)
        $Res = RunSSHCommands -HostName $HostName -Commands $Command
        $Command = ('grep "zdriver" {0} > {1}' -f $vmkload, $ZertoDriver)
        $Res = RunSSHCommands -HostName $HostName -Commands $Command -ExitStatusAction "Skip"
        
        $ExitStatus = $Res["0_exitStatus"]
        if ($ExitStatus -eq '0') {
            Write-Host "Zerto driver is loaded"
            return $true;
        }
        
        if ($ExitStatus -eq '1') {
            Write-Host "Zerto driver is not loaded"
            return $false;
        }
    }
}

Function GetDriverLogsFromHost
{
    <#
        .DESCRIPTION
        Copy Zerto driver logs from host to datastore
         
            .PARAMETER HostName
            Host Name to connect with SSH
             
            .PARAMETER DatastoreName
            Datastore Name
 
        .EXAMPLE
 
        GetDriverLogsFromHost -HostName <HostName> -DatastoreName <DatastoreName>
    #>

    
    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName
    )
    
    Write-Host "Starting $($MyInvocation.MyCommand)..."
    DownloadDriverLogFilesFromHostToPSEngine -HostName $HostName
    UploadDriverLogFilesFromPSEngineToDatastore -DatastoreName $DatastoreName -HostName $HostName
}

Function DownloadDriverLogFilesFromHostToPSEngine
{
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName
    )

    Write-Host "Starting $($MyInvocation.MyCommand)..."
        
    $sftpSessionId = ($SFTP_Sessions[$HostName]).Value.SessionId
    
    if (!(Test-Path -path $LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS)) {
        New-Item $LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS -Type Directory
        Write-Host "Create $LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS folder on powerShell engine"
    }
    
    $files = '/etc/vmware/zloadmod.txt',
             '/etc/vmware/zunloadmod.txt'
            
    foreach ($file in $files)
    {
        if (Test-SFTPPath -SessionId $sftpSessionId -Path $file) {
            Write-Host "Going to download $file from $HostName to powerShell engine ($LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS)"
            Get-SFTPItem -SessionId $sftpSessionId -Destination $LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS -Path $file -Force
            Write-Host "$file was copied from $HostName to powerShell engine"
        }
        else {
            Write-Host "File $file doesn't exist on $HostName"
        }
    }
}

Function UploadDriverLogFilesFromPSEngineToDatastore
{
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName
    )

    Write-Host "Starting $($MyInvocation.MyCommand)..."

    $psDriverName = "ds"
    $zertoDriverLogsDSPath = ('{0}:\zertoDriverLogsFromHost\{1}\' -f $psDriverName, $HostName)
    $zertoDriverLogsPSPath = ('{0}*' -f $LOCAL_TEMP_FOLDER_FOR_ZERTO_DRIVER_LOGS)
    $datastore = Get-Datastore $DatastoreName

    New-PSDrive -Location $datastore -Name $psDriverName -PSProvider VimDatastore -Root "\" -ErrorAction Stop
    Copy-DatastoreItem -Item $zertoDriverLogsPSPath -Destination $zertoDriverLogsDSPath -Force -ErrorAction Stop
    
    $files = (Get-ChildItem -Path $zertoDriverLogsDSPath -Name)  -join ";"
    Write-Host "$MyInvocation.MyCommand - ZertoDriverFiles: {$files} were copied from powerShell engine ($zertoDriverLogsPSPath) to datastore ($zertoDriverLogsDSPath)"
    
    Remove-PSDrive -Name $psDriverName
}

Function GetZertoFilesListFromHost
{
    <#
        .DESCRIPTION
        Return a list of all Zerto files on host under /var/zerto
         
            .PARAMETER HostName
            Host Name to connect with SSH
 
        .EXAMPLE
 
        GetZertoFilesListFromHost -HostName <HostName>
    #>

    
    [CmdletBinding()]
    [AVSAttribute(5, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName 
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        $Command = ('ls -l {0}' -f $ZERTO_FOLDER_ON_HOST)
        return RunSSHCommands -HostName $HostName -Commands $Command
    }
}


Function TestConnection {
    return "TestConnection"
}

Function CreateZertoUser {
    <#
        .DESCRIPTION
        Create a ZertoDR user and a ZertoDR role which includes required privileges.
        The script creates a permission by assigning the ZertoDR role to the ZertoDR user.
             
        .EXAMPLE
 
        CreateZertoUser
    #>

 
    Process{
        Write-Host "Starting $($MyInvocation.MyCommand)..."

        try {
            $zertoPrincipal = $DOMAIN + "\" + $ZERTO_USER_NAME

            #if the user already exists - do nothing
            if(IsZertoUserExists) {
                Write-Host "$ZERTO_USER_NAME already exists in $VC_ADDRESS. Skip creation..."
            }
            else {
                #Create Zerto user
                $PersistentSecrets.ZertoPassword = GenerateRandomPassword
                New-SsoPersonUser -UserName $ZERTO_USER_NAME -Password $PersistentSecrets.ZertoPassword -Description "Zerto DR user" -EmailAddress "ZertoDR@zerto.com" -FirstName "Zerto" -LastName "DR" -ErrorAction Stop
            
                # Add user to CloudAdmins group
                $group = "CloudAdmins"
                $SsoGroup = Get-SsoGroup -Name $group -Domain $DOMAIN
                Get-SsoPersonUser -Name $ZERTO_USER_NAME -Domain $DOMAIN -ErrorAction Stop | Add-UserToSsoGroup -TargetGroup $SsoGroup -ErrorAction Stop
                
                Write-Host "Finish creating ZertoUser ($ZERTO_USER_NAME)"
            }
        
            UpdateZertoRole
        }
        catch{
            Write-Error "Failed creating Zerto User, exception = $_" -ErrorAction Stop
        }
    }
}

Function UpdateZertoRole {
    <#
        .DESCRIPTION
        Updates a ZertoDR user with the latest ZertoDR role which includes required privileges.
        The script creates a permission by assigning the ZertoDR role to the ZertoDR user.
             
        .EXAMPLE
 
        UpdateZertoRole
    #>

    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $zertoPrincipal = $DOMAIN + "\" + $ZERTO_USER_NAME
        $zertoPrivileges = @(
          "Alarm.Create",
          "Alarm.Delete",
          "Authorization.ModifyPermissions",
          "Cryptographer.Access",
          "Datastore.AllocateSpace",
          "Datastore.Browse",
          "Datastore.Config",
          "Datastore.DeleteFile",
          "Datastore.FileManagement",
          "Datastore.UpdateVirtualMachineFiles",
          "StoragePod.Config",
          "Extension.Register",
          "Extension.Unregister",
          "Folder.Create",
          "Global.CancelTask",
          "Global.Diagnostics",
          "Global.DisableMethods",
          "Global.EnableMethods",
          "Global.LogEvent",
          "Host.Config.AdvancedConfig",
          "Host.Config.AutoStart",
          "Host.Config.Settings",
          "Host.Config.NetService",
          "Host.Config.Patch",
          "Host.Inventory.EditCluster",
          "Network.Assign",
          "Resource.AssignVAppToPool",
          "Resource.AssignVMToPool",
          "Resource.ColdMigrate",
          "Resource.HotMigrate",
          "Sessions.ValidateSession",
          "Task.Create",
          "Task.Update",
          "VApp.ApplicationConfig",
          "VApp.AssignResourcePool",
          "VApp.AssignVM",
          "VApp.Create",
          "VApp.Delete",
          "VApp.Import",
          "VApp.PowerOff",
          "VApp.PowerOn",
          "VirtualMachine.Config.AddExistingDisk",
          "VirtualMachine.Config.AddNewDisk",
          "VirtualMachine.Config.AddRemoveDevice",
          "VirtualMachine.Config.AdvancedConfig",
          "VirtualMachine.Config.CPUCount",
          "VirtualMachine.Config.DiskExtend",
          "VirtualMachine.Config.EditDevice",
          "VirtualMachine.Config.ManagedBy",
          "VirtualMachine.Config.Memory",
          "VirtualMachine.Config.RawDevice",
          "VirtualMachine.Config.RemoveDisk",
          "VirtualMachine.Config.Resource",
          "VirtualMachine.Config.Settings",
          "VirtualMachine.Config.SwapPlacement",
          "VirtualMachine.Config.UpgradeVirtualHardware",
          "VirtualMachine.Interact.PowerOff",
          "VirtualMachine.Interact.PowerOn",
          "VirtualMachine.Inventory.CreateFromExisting",
          "VirtualMachine.Inventory.Create",
          "VirtualMachine.Inventory.Register",
          "VirtualMachine.Inventory.Delete",
          "VirtualMachine.Inventory.Unregister",
          "VirtualMachine.State.RemoveSnapshot"
        )

        if((IsZertoUserExists $ZERTO_USER_NAME) -eq $false) {
            Write-Host "$ZERTO_USER_NAME doesn't exists in $VC_ADDRESS..."
            throw "$ZERTO_USER_NAME doesn't exists in $VC_ADDRESS..."
        }

        If (IsZertoRoleExists) {
            $joinedPrivilgaes = ($zertoPrivileges -join ";")
            Write-Host "Role: $ZERTO_ROLE already exists. Overwrite it with the following privilages: $joinedPrivilgaes"
            
            Remove-VIRole -Role (Get-VIRole -Name $ZERTO_ROLE) -Force:$true -Confirm:$false
            New-VIRole -name $ZERTO_ROLE -Privilege (Get-VIPrivilege -Server $VC_ADDRESS -id $zertoPrivileges) -Server $VC_ADDRESS
        } 
        else {
            #Create a new role
            New-VIRole -name $ZERTO_ROLE -Privilege (Get-VIPrivilege -Server $VC_ADDRESS -id $zertoPrivileges) -Server $VC_ADDRESS -ErrorAction Stop
            Write-Host "Role $ZERTO_ROLE created on $VC_ADDRESS"
        }
        
        # Get the Root Folder
        $rootFolder = Get-Folder -NoRecursion
        # Create permission on vCenter object by assigning role to user
        New-VIPermission -Entity $rootFolder -Principal $zertoPrincipal -Role $ZERTO_ROLE -Propagate:$true -ErrorAction Stop
        Write-Host "Finish to update ZertoRole ($ZERTO_ROLE) for ZertoUser ($ZERTO_USER_NAME)"
    }
}

Function ResetZertoPassword
{
    <#
        .DESCRIPTION
        Change password for Zerto user
 
             
        .EXAMPLE
 
        ResetZertoPassword
    #>

    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $zertoUser = Get-SsoPersonUser -Name $ZERTO_USER_NAME -Domain $DOMAIN
        if($zertoUser) {
            $newPassword = GenerateRandomPassword
            Set-SsoPersonUser -User $zertoUser -NewPassword $newPassword -ErrorAction Stop
            $PersistentSecrets.ZertoPassword = $newPassword
            
            Write-Host "Password for $zertoUsername changed successfully"
        }
        else{
            throw "Error! Failed to reset Zerto password for $zertoUsername, user doesn't exist"
        }
    }
}

Function SetSSHTimeout{
    <#
        .DESCRIPTION
        Determines how long SSH session remains open
 
            .PARAMETER HostName
            Host Name to connect with SSH
             
            .PARAMETER SSHTimeout
            SSH timeout value
             
        .EXAMPLE
 
        SetSSHTimeout -HostName <HostName> -SSHTimeout <SSHTimeout>
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "SSH timeout value")]
        [string]$SSHTimeout
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $vmHost = Get-VMHost -Name $HostName
        
        Get-AdvancedSetting -Entity $vmHost -Name  "UserVars.ESXiShellInteractiveTimeOut" -ErrorAction SilentlyContinue | Set-AdvancedSetting -Value $SSHTimeout -Confirm:$false -ErrorAction SilentlyContinue
        Write-Host "Set configuration setting ""UserVars.ESXiShellInteractiveTimeOut"" on $HostName to $SSHTimeout"
    }
}


Function VerifyAndUploadFilesFromPSEngineToHost {
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName
    )
    
    Write-Host "Starting $($MyInvocation.MyCommand)..."
    
    CreateZertoFolderOnHost -HostName $HostName
    
    foreach ($file in Get-ChildItem $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES* -Include *.sh, *.o) {
        $signature = ("{0}_signature" -f $file)

        if ((VerifyFileBySignature -FilePath $file -SignatureFilePath $signature)){
             Set-SFTPItem -SessionId ($SFTP_Sessions[$HostName]).Value.SessionId -Destination $ZERTO_FOLDER_ON_HOST -Path $file -Force
        }
        else{
            throw "Error! host $HostName failed to verify $file with $signature, openSSL output: $isVerified"
        }
    }
}

Function DownloadFilesFromDatastoreToPSEngine {
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")]
        [string]$BiosUuid
    )
    
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    $psDriverName = "ds"
    $FullRemoteFileLocation = ('{0}:\zagentid\{1}\*' -f $psDriverName, $BiosUuid)
    $datastore = Get-Datastore $DatastoreName

    Write-Host "Going to download files from $darastore ($FullRemoteFileLocation) to PS engine ($LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES)."
    
    New-PSDrive -Location $datastore -Name $psDriverName -PSProvider VimDatastore -Root "\" -ErrorAction Stop
    Copy-DatastoreItem -Item $FullRemoteFileLocation -Destination $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES -Force -ErrorAction Stop
    
    $files = (Get-ChildItem -Path $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES -Name)  -join ";"
    Write-Host "ZertoFiles: {$files} were copied from darastore $DatastoreName ($FullRemoteFileLocation) to PS engine ($LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES)"
    
    Remove-PSDrive -Name $psDriverName
}

Function CopyFilesFromDatastoreToHost {
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")]
        [string]$BiosUuid
    )
    
    Write-Host "Starting $($MyInvocation.MyCommand)..."
    
    DownloadFilesFromDatastoreToPSEngine -DatastoreName $DatastoreName -BiosUuid $BiosUuid
    VerifyAndUploadFilesFromPSEngineToHost -HostName $HostName
}

Function RunSSHCommands {
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Commands to execute")]
        [String[]]$Commands,
        [Parameter(Mandatory = $false, 
            HelpMessage = "Action on exitStatus 1")]
        [string]$ExitStatusAction = "Stop"
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $NamedOutputs = @{}
        Set-Variable -Name NamedOutputs -Value $NamedOutputs -Scope Global
        
        $i = 0
        foreach ($Command in $Commands) {
            $SSH = Invoke-SSHCommand -SSHSession $SSH_Sessions[$HostName].Value -Command $Command

            if (!$SSH) { 
                throw "Error! failed to Invoke-SSHCommand ""$Command"" on host $HostName"
            }

            $ExitStatus = $SSH.ExitStatus
            $Error = $SSH.Error
            $Output = ($SSH.Output -join ";")

            if ($ExitStatus -ne 0 -Or $Error) {
                if (($ExitStatus -eq 1) -And (!$Error) -And ($ExitStatusAction -eq "Skip")) {
                    Write-Host "ExitStatus of ""$Command"" is 1, while ExitStatusAction = Skip. Skipping..."
                }
                else {
                    throw "Error! failed to run ""$Command"" on host $HostName, ExitStatus: $ExitStatus, Output: $Output, Error: $Error, ExitStatusAction: $ExitStatusAction"
                }
            }
            
            Write-Host "Finish to run ""$Command"" on host $HostName, ExitStatus: $ExitStatus, Output: $Output, Error: $Error"
            
            $NamedOutputs["$($i)_cmd"] = $Command
            $NamedOutputs["$($i)_exitStatus"] = $ExitStatus
            $NamedOutputs["$($i)_output"] = $Output
            $NamedOutputs["$($i)_error"] = $Error

            $i++;
        }
        
        return $NamedOutputs
    }
}

Function Get-HostTempFolderInfo {
    <#
        .DESCRIPTION
          
        Display information about the available disk space (For Internal Use)
          
            .PARAMETER HostName
            Host Name to connect with SSH
          
        .EXAMPLE
          
        Get-HostTempFolderInfo -HostName xxx.xxx.xxx.xxx
    #>

    [CmdletBinding()]
    [AVSAttribute(5, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName 
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $Command = "vdf"
        return RunSSHCommands -HostName $HostName -Commands $Command
    }
}

Function EnsureConnectivity {   
    <#
        .DESCRIPTION
          
        Check if the host is up and running (For Internal Use)
          
            .PARAMETER HostName
            Host Name to connect with SSH
          
        .EXAMPLE
          
        EnsureConnectivity -HostName xxx.xxx.xxx.xxx
    #>

    [CmdletBinding()]
    [AVSAttribute(5, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName 
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $Command = "echo testing123"
        return RunSSHCommands -HostName $HostName -Commands $Command
    }
}

Function Get-HostEsxiVersion {
    <#
        .DESCRIPTION
          
        Retrieve the ESXi version (For Internal Use)
          
            .PARAMETER HostName
            Host Name to connect with SSH
          
        .EXAMPLE
          
        Get-HostEsxiVersion -HostName xxx.xxx.xxx.xxx
    #>

    [CmdletBinding()]
    [AVSAttribute(5, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName 
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        $Command = "vmware -l"
        return RunSSHCommands -HostName $HostName -Commands $Command
    }
}

Function ChangeStartupFile {
    <#
        .DESCRIPTION
          
        Responsible for loading the driver when the host is booting.
        /etc/rc.local.d/local.sh file is executed after all the normal system services are started
          
            .PARAMETER HostName
            Host Name to connect with SSH
          
            .PARAMETER DatastoreName
            Datastore Name
          
            .PARAMETER BiosUuid
            "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid"
          
        .EXAMPLE
          
        ChangeStartupFile -HostName xxx.xxx.xxx.xxx -DatastoreName xxx -BiosUuid xxx
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")]    
        [string]$BiosUuid,
        [Parameter(Mandatory = $true,    
            HelpMessage = "Zerto driver memory size in MB")]    
        [string]$DriverMemoryInMB,
        [Parameter(Mandatory = $true,    
            HelpMessage = "Use explicit argument for zloadmod script (True / False)")]    
        [string]$UseExplicitDriverArgs
    )

    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        if(((ValidateDatastoreName -DatastoreName $DatastoreName) -ne $true) -or ((ValidateBiosUUID -DatastoreName $DatastoreName -BiosUuid $BiosUuid) -ne $true) -or ((ValidateDigitsOnly -InputString $DriverMemoryInMB) -ne $true))
        {
            throw "validation failed"
        }
        
        $zloadmod = ('{0}/zloadmod.sh' -f $ZERTO_FOLDER_ON_HOST)

        CopyFilesFromDatastoreToHost -HostName  $HostName -DatastoreName $DatastoreName -BiosUuid $BiosUuid

        $startupFile = ('{0}/startup_file.sh' -f $ZERTO_FOLDER_ON_HOST)
    
        if($UseExplicitDriverArgs -eq $true)
        {
            $driverArgs = "load -ds $DatastoreName -uid $BiosUuid -mem $DriverMemoryInMB -avs"
        }
        else
        {
            $driverArgs = "load $DatastoreName $BiosUuid 0 `"`" 1"
        }
        
        $Commands =    ('grep -v "ZeRTO\|exit 0" /etc/rc.local.d/local.sh > {0}' -f $startupFile),
        ('echo \#ZeRTO\ >> {0}' -f $startupFile),
        ('echo sh {0} {1} \> /etc/vmware/zloadmod.txt \2\>\&\1 \#ZeRTO\ >> {2}' -f $zloadmod, $driverArgs, $startupFile),
        ('echo \#ZeRTO\ >> {0}' -f $startupFile),
        ('echo "exit 0" >> {0}' -f $startupFile),
        ('cp -f {0} /etc/rc.local.d/local.sh' -f $startupFile),
        ('chmod a+x {0}' -f $zloadmod)
                    
        return RunSSHCommands -HostName $HostName -Commands $Commands
    }
}

Function InstallDriver {
    <#
        .DESCRIPTION
          
        Install the driver
          
            .PARAMETER HostName
            Host Name to connect with SSH
              
            .PARAMETER DatastoreName
            Datastore Name
          
            .PARAMETER BiosUuid
            Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid
          
            .PARAMETER EsxiVersion
            Esxi version
          
        .EXAMPLE
        InstallDriver -HostName xxx.xxx.xxx.xxx -DatastoreName <DatastoreName> -BiosUuid <UUID> -EsxiVersion xx
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, 
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true, 
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true,
            HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")]    
        [string]$BiosUuid,
        [Parameter(Mandatory = $true,    
            HelpMessage = "Esxi version")]    
        [string]$EsxiVersion,
        [Parameter(Mandatory = $true,    
            HelpMessage = "Driver memory in MB for Zerto driver")]    
        [string]$DriverMemoryInMB,
        [Parameter(Mandatory = $true,    
            HelpMessage = "Use explicit argument for zloadmod script (True / False)")]    
        [string]$UseExplicitDriverArgs
    )

    Process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        if(((ValidateDatastoreName -DatastoreName $DatastoreName) -ne $true) -or ((ValidateBiosUUID -DatastoreName $DatastoreName -BiosUuid $BiosUuid) -ne $true) -or ((ValidateDigitsOnly -InputString $EsxiVersion) -ne $true) -or ((ValidateDigitsOnly -InputString $DriverMemoryInMB) -ne $true))
        {
            throw "validation failed"
        }
        
        if (IsZertoDriverLoaded $HostName)
        {
            Write-Host "Warning! Zerto driver is already loaded on $HostName"
        }
        
        $zloadmod = ('{0}/zloadmod.sh' -f $ZERTO_FOLDER_ON_HOST)
        
        CopyFilesFromDatastoreToHost -HostName  $HostName -DatastoreName $DatastoreName -BiosUuid $BiosUuid
        
        if($UseExplicitDriverArgs -eq $true)
        {
            $driverArgs = "init -ds $DatastoreName -uid $BiosUuid -ver $EsxiVersion -mem $DriverMemoryInMB -avs";
        }
        else
        {
            $driverArgs = "init $DatastoreName $BiosUuid 0 $EsxiVersion 1";
        }
        
        $Commands = ('chmod a+x {0}' -f $zloadmod),
                    ('{0} {1} > /etc/vmware/zloadmod.txt' -f $zloadmod, $driverArgs)

        return RunSSHCommands -HostName $HostName -Commands $Commands
    }
}

Function UninstallDriver {
    <#
        .DESCRIPTION
          
        Uninstall the driver
          
            .PARAMETER HostName
            Host Name to connect with SSH
              
            .PARAMETER DatastoreName
            Datastore Name
          
            .PARAMETER BiosUuid
            Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid
          
          
        .EXAMPLE
        UninstallDriver -HostName xxx.xxx.xxx.xxx -DatastoreName <DatastoreName> -BiosUuid <UUID>
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true,
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true,
            HelpMessage = "Datastore Name")]
        [string]$DatastoreName,
        [Parameter(Mandatory = $true,
            HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")]
        [string]$BiosUuid
    )
    
    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        if (IsZertoDriverLoaded $HostName)
        {
            $zunloadmod = ('{0}/zunloadmod.sh' -f $ZERTO_FOLDER_ON_HOST)
            
            CopyFilesFromDatastoreToHost -HostName  $HostName -DatastoreName $DatastoreName -BiosUuid $BiosUuid

            $Commands = ('chmod a+x {0}' -f $zunloadmod),
                        ('{0} cleanup > /etc/vmware/zunloadmod.txt' -f $zunloadmod)

            return RunSSHCommands -HostName $HostName -Commands $Commands
        }
        
        else {
            throw "Error! Failed to run UninstallDriver, Zerto driver is not loaded on $HostName."
        }
    }
}

Function Install-Zerto {
    <#
        .DESCRIPTION
          
        Install Zerto Applience
 
            .PARAMETER MyZertoToken
            My Zerto token for downloading the OVA and signature file
              
            .PARAMETER HostName
            Host Name deploy OVA on
 
            .PARAMETER ZVMLIp
            IP of the ZVM applience
              
            .PARAMETER DatastoreName
            Datastore Name
 
            .PARAMETER AzureTenantId
            Azure Tenant ID
 
            .PARAMETER AzureClientID
            Azure Client ID
              
            .PARAMETER AvsClientSecret
            AVS Client Secret
 
            .PARAMETER AvsSubscriptionId
            AVS Subscription ID
 
            .PARAMETER AvsResourceGroup
            Avs Resource Group
              
            .PARAMETER AvsCloudName
            Avs Cloud Name
 
            .PARAMETER SubnetMask
            Subnet Mask of the applience network
 
            .PARAMETER DefaultGateway
            Default Gateway of the applience network
              
            .PARAMETER DNS
            DNS name server for the applience
          
        .EXAMPLE
        Install-Zerto -MyZertoToken <MyZertoToken> -HostName xxx.xxx.xxx.xxx -ZVMLIp xxx.xxx.xxx.xxx -DatastoreName <DatastoreName> -AzureTenantId <AzureTenantId>
         -AzureClientID <AzureClientID> -AvsClientSecret ********* -AvsSubscriptionId <AvsSubscriptionId> -AvsResourceGroup <AvsResourceGroup> -AvsCloudName <AvsCloudName>
         -.DefaultGateway xxx.xxx.xxx.xxx -SubnetMask xxx.xxx.xxx.xxx -DNS xxx.xxx.xxx.xxx
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, HelpMessage = "Token for MyZerto")]
        [ValidateNotNullOrEmpty()][string]
        $MyZertoToken,

        [Parameter(Mandatory = $false, HelpMessage = "VC host name to put the machine on")]
        [string]
        $HostName,

        [Parameter(Mandatory = $true, HelpMessage = "Zvm IP address")]
        [ValidateNotNullOrEmpty()][string]
        $ZVMLIp,

        [Parameter(Mandatory = $true, HelpMessage = "DatastoreName")]
        [ValidateNotNullOrEmpty()][string]
        $DatastoreName,

        [Parameter(Mandatory = $true, HelpMessage = "Azure Tenant Id, Globally unique identifier, found in Azure portal")]
        [ValidateNotNullOrEmpty()][string]
        $AzureTenantId, 

        [Parameter(Mandatory = $true, HelpMessage = "Azure Client ID - Application ID, found in Azure portal")]
        [ValidateNotNullOrEmpty()][string]
        $AzureClientID, 

        [Parameter(Mandatory = $true, HelpMessage = "Enables authentication to Azure Active Directory using a client secret")]
        [ValidateNotNullOrEmpty()][SecureString]
        $AvsClientSecret,

        [Parameter(Mandatory = $true, HelpMessage = "The ID of the target subscription")]
        [ValidateNotNullOrEmpty()][string]
        $AvsSubscriptionId, 

        [Parameter(Mandatory = $true, HelpMessage = "AWS resources that are all in the same AWS Region")]
        [ValidateNotNullOrEmpty()][string]
        $AvsResourceGroup, 

        [Parameter(Mandatory = $true, HelpMessage = "Private cloud name")]
        [ValidateNotNullOrEmpty()][string]
        $AvsCloudName, 

        [Parameter(Mandatory = $true, HelpMessage = "SubnetMask address")]
        [ValidateNotNullOrEmpty()][string]
        $SubnetMask,

        [Parameter(Mandatory = $true, HelpMessage = "Default gateway")]
        [ValidateNotNullOrEmpty()][string]
        $DefaultGateway,

        [Parameter(Mandatory = $true, HelpMessage = "DNS server address")]
        [ValidateNotNullOrEmpty()][string]
        $DNS,

        [Parameter(Mandatory = $false, HelpMessage= "Configure ovf properties")]
        [bool]
        $ConfigureOvfProperties = $false
    )

    Process {
        
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        CreateZertoUser
                
        $HostName = GetAndValidateHostName -HostName $HostName
       
        ValidateVcEnvParams -DatastoreName "$DatastoreName" -ZVMLIp "$ZVMLIp" -SubnetMask "$SubnetMask" -DefaultGateway "$DefaultGateway" -DNS "$DNS"

        $OvaFilePath = GetZertoOVAFile -MyZertoToken $MyZertoToken

        try {
            DeployVm -OvaPath $OvaFilePath -VMHostName $HostName -DatastoreName $DatastoreName -ZVMLIp $ZVMLIp -SubnetMask $SubnetMask -DefaultGateway $DefaultGateway -DNS $DNS -AzureTenantId $AzureTenantId -AzureClientID $AzureClientID -AvsClientSecret $AvsClientSecret -AvsSubscriptionId $AvsSubscriptionId -AvsResourceGroup $AvsResourceGroup -AvsCloudName $AvsCloudName -ConfigureOvfProperties $ConfigureOvfProperties
        }
        catch
        {
            Write-Error "Failed to deploy $ZVM_VM_NAME, exception = $_" 
            Uninstall-Zerto -Confirmation "yes"
            Write-Error "Finish cleanup environment" -ErrorAction Stop
        }

        StartZVM
    }
}

Function Uninstall-Zerto {
    <#
        .DESCRIPTION
          
        Uninstall Zerto Applience
          
        .PARAMETER Confirmation
        Confirmation for uninstalling Zerto Applience that check if the input is "yes"
 
 
        .EXAMPLE
        Uninstall-Zerto -Confirmation "yes"
    #>

    [CmdletBinding()]
    [AVSAttribute(30, UpdatesSDDC = $false)]
    param(
        [Parameter(Mandatory = $true, HelpMessage = "Confirmation for unistall, type yes to confirm.")]
        [ValidateNotNullOrEmpty()][string]
        $Confirmation
    )

    Process {
            
        Write-Host "Starting $($MyInvocation.MyCommand)..."
        
        if ($Confirmation -ne "yes"){
            Write-Error "Need to confirm uninstall by typing 'yes'" -ErrorAction Stop
        }
    
        if((IsVmExists -VmName $ZVM_VM_NAME) -eq $true)
        {
            $VM = Get-VM $ZVM_VM_NAME
        
            if($VM.PowerState -eq "PoweredOn")
            {
                Write-Host "Stoping $ZVM_VM_NAME VM"
                Stop-VM -VM $VM -Confirm:$False -ErrorAction Stop
            }

            Write-Host "Deleting $ZVM_VM_NAME VM from disk"
            Remove-VM -VM $VM -DeletePermanently -confirm:$false
            
            Write-Host "Finished uninstalling Zerto..."
        }
        else
        {
            Write-Error "$ZVM_VM_NAME doesn't exist, failed to uninstall Zerto"
        }
    }
}