jetdr_avs_lib.ps1

trap
{
    Write-Host "$_"
    exit 1
}

function download_jetdr_bundle{
    param(
        $url,
        $download_file_path #"f:\tools\" + $zip_filename
    )
    Write-Host "$url, $download_file_path"
    $download_path = Split-Path $download_file_path
    if(!(Test-Path -path $download_path))
    {
    write-error "`n[ERROR] The path $download_path doesn't exist. Please create the folder and try again`n" -ErrorAction Stop
    }
    if(Test-Path -path $download_file_path)
    {
        Write-Host "File already exists. Skipping download."
    }
    else {
        Write-Host "### $($MyInvocation.MyCommand) ###"
        $start_time = Get-Date
        Invoke-WebRequest $url -OutFile $download_file_path -Resume 
        if (!$?) {
        write-error "Downloading jetdr bundle failed, rc=$?" -ErrorAction Stop
        }
        Write-Host "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
    }
}

function unzip_ova {
    param(
        $zip_filename, #JSDR-GA.2.7.zip
        $destination_folder
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    # delete the extract directory if it exists
    if(Test-Path -path $destination_folder)
    {
        Write-Host "Destination directory $destination_folder already exists, deleting."
        Remove-Item $destination_folder -Recurse
    }
    try {
        Expand-Archive -Path $zip_filename -DestinationPath $destination_folder
    }
    catch {
        write-host "Unzip failed. The downloaded file may be corrupt. Deleting. Please run the script again."
        Remove-Item $zip_filename
    write-error "Unzip jetdr bundle failed, rc=$?" -ErrorAction Stop
    }
    $checksumfile = Get-ChildItem -Path $destination_folder -Filter "*checksum*txt" -Recurse | %{$_.FullName}
    $ovafile = Get-ChildItem -Path $destination_folder -Filter "*jetstream*ova" -Recurse | %{$_.FullName}
    $filehash = Get-FileHash $ovafile -Algorithm SHA256
    $checksum = Select-String -Path $checksumfile -Pattern $filehash.hash
    if ($checksum -ne $null) {
        write-host "Ova checksum validated successfully"
    }
    else{
        write-host "Checksum validation failed. The downloaded file may be corrupt. Deleting. Please run the script again."
        Remove-Item $zip_filename
        write-error "Ova checksum validation failed." -ErrorAction Stop
    }
}

function ms_rest_state {
    param(
        $ms_ip,
        $ms_user,
        $ms_pwd,
        $timeout = 600
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $rlt = 0
    while ($timeout -gt 0)
    {
        $resp_obj = get_ms_state $ms_ip $ms_user $ms_pwd
        if($resp_obj -eq $null){
            Write-Host "Unable to get MSA state, sleep for 10 seconds.."
            start-sleep 10
        }
        else
        {
            Write-Host "MSA REST server is up and running"
            $rlt = 1
            break
        }
        $timeout -= 10
    }
    return $rlt
}

function deploy_ova {
    param(
        $ovf_path,
        $ms_network,
        $ms_hostname,
        $ms_pwd,
        $ms_gateway,
        $ms_dns,
        $ms_ip,
        $ms_netmask,
        $ms_cluster_name,
        $ms_vm_name,
        $ms_datastore,
        $disk_format="Thin",
        $disable_ssh=$true
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $ovfConfig = Get-OvfConfiguration -Ovf $ovf_path
    $ovfConfig.NetworkMapping.Network_1.Value = $ms_network
    $ovfConfig.Common.hostname.Value = $ms_hostname
    $ovfConfig.Common.password.Value = $ms_pwd
    $ovfConfig.Common.disablessh.Value = $disable_ssh
    $ovfConfig.vami.JetStream_Software_Disaster_Recovery_Management_Server.gateway.Value = $ms_gateway
    $ovfConfig.vami.JetStream_Software_Disaster_Recovery_Management_Server.DNS.Value = $ms_dns
    $ovfConfig.vami.JetStream_Software_Disaster_Recovery_Management_Server.ip0.Value = $ms_ip
    $ovfConfig.vami.JetStream_Software_Disaster_Recovery_Management_Server.netmask0.Value = $ms_netmask

    $cluster = Get-Cluster -Name $ms_cluster_name
    $disk_format = "Thin"
    $vm_name = $ms_vm_name
    $datastore = Get-Datastore -Name $ms_datastore
    $vmhost = Get-Cluster $ms_cluster_name | Get-VMHost | Select -first 1
    try {
        Import-VApp -Source $ovf_path -OvfConfiguration $ovfConfig -Name $vm_name -vmHost $vmhost -Location $cluster -Datastore $datastore -DiskStorageFormat $disk_format -Confirm:$false > $null
    }
    catch {
    write-error "Import MS ova failed, rc=$?" -ErrorAction Stop
    }

    start-vm $ms_vm_name -confirm:$false > $null
    if (!$?) {
    write-error "Starting VM $ms_vm_name failed" -ErrorAction Stop
    }

    $vm_ip=get_vm_ip $ms_vm_name 600
    $rc=ms_rest_state $vm_ip "root" $ms_pwd 600
    if (!$rc) {
    write-error "MSA $vm_ip is not ready after 600 seconds" -ErrorAction Stop
    }
    Write-Host "Sleep for 2 more minute after Manamgement server REST server is running"
    start-sleep 120
    return $vm_ip
}

function ping_vm
{
    param(
        $vm_ip = $( throw "specify vm ip" ),
        $timeout = 600
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"

    $rlt = 0
    while ($timeout -gt 0)
    {
        $rc = test-connection -computername $vm_ip -quiet
        if (!$rc)
        {
            Write-Host "$vm_ip is not reachable, sleep for 5 seconds.."
            start-sleep 5
        }
        else
        {
            Write-Host "$vm_ip is pingable now."
            $rlt = 1
            break
        }
        $timeout -= 5
    }
    return $rlt
}

function get_vm_ip {
    param(
    $vm_name = $(throw "vmName is required"),
    $timeout = 600
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"

    while ($timeout -gt 0) {
        $vm_ip = (Get-VM $vm_name).Guest.IPAddress[0]
        if (($vm_ip -ne $null) -and ($vm_ip -match "\d+\.\d+\.\d+\.\d+")) {
            $timeout = 0
        } else {
            $timeout -= 30
            Write-Host "couldn't get ip of $vm_name. Sleep for 30 seconds"
            Start-Sleep 30
        }
    }
    if (($vm_ip -eq $null) -or ($vm_ip -notmatch "\d+\.\d+\.\d+\.\d+")) {
        write-error "Failed in getting ip of vm $vm_name" -ErrorAction Stop
    }
    return $vm_ip
}

function create_user {
    param(
            [String]$user, # e.g "testuser"
            [String]$pwd
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $user=New-SsoPersonUser -UserName $user -Password $pwd
    if(!$user) {
        write-error "Failed to create user $user" -ErrorAction Stop
    }
}

function delete_user {
    param(
            [String]$user
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $domain = $user.split("\")[0]
    $username = $user.split("\")[1]
    $ssouser = Get-SsoPersonUser -Name $username -Domain $domain
    Remove-SsoPersonUser -User $ssouser[0]
}

function assign_new_role_to_user {
    param(
        [String]$new_role_name,
        [String]$sso_user_name, # e.g "vsphere.local\testuser"
        [String[]]$new_privileges # e.g "Maintenance", "Query patch", "CIM interaction", "Profile-driven storage update"
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $existing_role = "CloudAdmin"

    # chk if Cloudadmin role exists
    $cloudadmin_role = Get-VIRole | Where-Object {$_.Name -eq $existing_role}

    $cloudadmin_privileges = Get-VIPrivilege -Role $existing_role

    # check if new role already exists
    $new_role = Get-VIRole | Where-Object {$_.Name -eq $new_role_name}
    if ($new_role -ne $null) {
        #remove_role $new_role_name
            Write-Host "Role $new_role_name already exists in vCenter. Skipping creation"
    }
        else{
        # create a new role with cloudadmin privileges
        $my_role = New-VIRole -Privilege $cloudadmin_privileges -Name $new_role_name
        if (!$?) {
                write-error "Failed to create new role ${new_role_name}, rc=$?" -ErrorAction Stop
            }
    }
        $my_role = Get-VIRole | Where-Object {$_.Name -eq $new_role_name}
    # add new privileges to this new role
    $priv = @()
    Foreach ($cust_priv in $new_privileges){
        $priv += Get-VIPrivilege | Where-Object {$_.Name -eq $cust_priv}
    }

    Set-VIRole -Role $my_role -AddPrivilege $priv
    if (!$?) {
        write-error "Failed to add new privileges ${priv} to role ${my_role}, rc=$?" -ErrorAction Stop
    }

    # get root folder and then grant $sso_user_name access to the role on rootfolder level
    Write-Host "assign permissions"
    $rootfolder = Get-Folder -NoRecursion
    $new_permission = New-VIPermission -Role $new_role_name -Principal $sso_user_name -Entity $rootfolder
    if (!$?) {
        write-error "Failed to grant user ${sso_user_name} to role ${new_role_name}, rc=$?" -ErrorAction Stop
    }
    Write-Host "new permissions type: " + $new_permission
    return $new_permission
}

function remove_role {
    param(
        $role_name
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    Write-Host "Removing role $role_name"
    # remove a role
    Remove-VIRole $role_name -confirm:$false -Force
    if (!$?) {
        write-error "Failed to remove role ${role_name}, rc=$?" -ErrorAction Stop
    }
    return
}

function remove_permissions {
    param($permissions)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    Remove-VIPermission $permissions -confirm:$false
    if (!$?) {
        write-error "Failed to remove permissions ${permissions}, rc=$?" -ErrorAction Stop
    }
}

function generate_password {
    param(
        [ValidateRange(8, 20)]
        [int]
        $length = 14
    )
    $symbols = '!@#$%^&*'.ToCharArray()
    $characterList = 'a'..'z' + 'A'..'Z' + '0'..'9' + $symbols
    do {
        $password = -join (0..$length | % { $characterList | Get-Random })
        [int]$hasLowerChar = $password -cmatch '[a-z]'
        [int]$hasUpperChar = $password -cmatch '[A-Z]'
        [int]$hasDigit = $password -match '[0-9]'
        [int]$hasSymbol = $password.IndexOfAny($symbols) -ne -1

    }
    until (($hasLowerChar + $hasUpperChar + $hasDigit + $hasSymbol) -ge 4)

    return $password
}

function get_request {
    param(
        $endpoint,
        $header,
        $outfile="get_request_output.csv"
    )
    Write-Host "### $($MyInvocation.MyCommand) ###"
    try {
        $resp_obj = Invoke-RestMethod -Method 'Get' -Uri $endpoint -Headers $header -skipCertificateCheck
    } catch {
        # Dig into the exception to get the Response details.
        # Note that value__ is not a typo.
        $_.Exception |format-list -force
    }
    return $resp_obj
}

function get_dr_header {
    param($fios_session_id)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $header = @{
        'Accept' = "application/json"
        'content-type' = 'application/json'
        'fios-session' = $fios_session_id
    }
    return $header
}

function post_request {
    param(
        $endpoint,
        $header,
        $body,
        $outfile="post_request_output.csv"
    )

    try {
        $resp_obj = Invoke-RestMethod -Method 'Post' -Uri $endpoint -Headers $header -Body $body -skipCertificateCheck
    } catch {
        $_.Exception | format-list -force
    }
    return $resp_obj
}

function get_encoded_auth {
    param($user, $pwd)
    $plain_str = "${user}:${pwd}"
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($plain_str)
    $encoded_text =[Convert]::ToBase64String($bytes)
    $encoded_text
}

function get_vcenter_session {
    param($ms_ip, $vcenter_fqdn, $base64_cred)

    # https://<MS hostname>:8443/jss/domains
    $endpoint = 'https://' + $ms_ip + ':8443/jss/vmplatform/' + $vcenter_fqdn + "/loginByAddress"
    $header = @{
        'Accept' = "application/json"
        'Authorization' = 'Basic ' + $base64_cred
    }

    $resp_obj = get_request $endpoint $header
    #$resp_obj | Out-String | Write-Host
    if ($resp_obj -match '=\"(.*?)\"') {
        $fios_session = $matches[1]
    } else {
        write-error "Failed to get vcenter session id. Please check if MSA is registered to vCenter and credentials are correct." -ErrorAction Stop
    }
    return $fios_session
}


function refresh_vc_session {
    param($ms_ip, $vc_hostname, $vc_user, $vc_pwd)
    $vc_base64 = get_encoded_auth $vc_user $vc_pwd
    $fios_session_id = get_vcenter_session $ms_ip $vc_hostname $vc_base64
    return $fios_session_id
}


function login_ms_portal {
    param($ms_ip, $ms_user, $ms_pwd)
    $timeout = 600
    $ms_base64 = get_encoded_auth $ms_user $ms_pwd
    $request_url = 'https://' + $ms_ip + ':8443/jss/ms/login'
    $header = @{
        "Authorization" = "Basic " + $ms_base64
    }
    Write-Host $request_url
    while ($timeout -gt 0) {
        try {
            $resp_obj = get_request $request_url $header
            $session_id = $resp_obj.loginResponse.sessionId
        } catch {
            $_.Exception |format-list -force
        }
        if ($session_id -ne $null) {
            return $session_id
        } else {
            Write-host "Waiting get MSA session id. Sleep for 1 minute and retry"
            $timeout -= 60
            start-sleep 60
        }
    }
    if ($session_id -eq $null) {
        Write-Error "Failed to get MSA session id after 10 minutes. Please check your MSA" -ErrorAction Stop
    }
}

function register_ms_with_vc {
    param($ms_ip, $ms_user, $ms_pwd, $vc_hostname, $vc_user, $vc_pwd, $ms_mode, $register_with_ip)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    Write-Host "Registering vCenter $vc_hostname to MS $ms_ip with user $vc_user"
    $endpoint = 'https://' + $ms_ip + ':8443/jss/vmplatform'
    $vc_base64 = get_encoded_auth $vc_user $vc_pwd
    $header = @{
        "Accept" = "application/json"
        'content-type' = 'application/json'
        'ms-session' = login_ms_portal $ms_ip $ms_user $ms_pwd
    }
    $body = @{
        "address" = $vc_hostname
        "credential" = $vc_base64
        "msMode" = $ms_mode
        "registerWithMsIp" = $register_with_ip
    }
    $json = $body | Convertto-JSON
    $resp_obj = post_request $endpoint $header $json
}

function unregister_vcenter_from_ms {
    param($ms_ip, $vc_hostname, $vc_user, $vc_pwd, $fios_session_id)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    Write-Host "Unregistering vCenter $vc_hostname from MS $ms_ip"
    $endpoint = 'https://' + $ms_ip + ':8443/jss/vmplatform/' + $vc_hostname + "/unregisterByAddress"
    $vc_base64 = get_encoded_auth $vc_user $vc_pwd
    $body = @{
        "credential" = $vc_base64
    }
    $json = $body | Convertto-JSON
    $header = get_dr_header($fios_session_id)
    $resp_obj = post_request $endpoint $header $json
}

function get_drvas {
    param($ms_ip, $fios_session_id)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $endpoint = 'https://' + $ms_ip + ':8443/jss/drvas'
    $header = get_dr_header($fios_session_id) 
    $resp_obj = get_request $endpoint $header
    $resp_obj
    if ($resp_obj -eq $null) {
        Write-Host "No DRVA is configured."
    }
    else {
        Write-Error "DRVA(s) are still configured. Please unconfigure and try again" -ErrorAction Stop
    }
}

function get_site_dc_state {
    param(
        $ms_ip, $fios_session_id
    )

    $endpoint = 'https://' + $ms_ip + ":8443/jss/datacenters"
    $header = get_dr_header($fios_session_id)
    $primary_site_registered_vc = get_request $endpoint $header
    Write-Host "request_url: $endpoint"
    return $primary_site_registered_vc
}

function get_cluster_state {
    param(
        $ms_ip, $fios_session_id, $cluster_name
    )

    $primary_site_dcs = get_site_dc_state $ms_ip $fios_session_id
    foreach ($dc in $primary_site_dcs) {
        foreach ($cluster in $dc.clusters) {
            if ($cluster.clusterName.ToLower() -eq $cluster_name.ToLower()) {
                $cluster_id = $cluster.id
            }
        }
    }

    $endpoint = 'https://' + $ms_ip + ":8443/jss/clusters/" + $cluster_id
    $header = get_dr_header($fios_session_id)
    $cluster_state = get_request $endpoint $header
    Write-Host "request_url: $endpoint"
    return $cluster_state

}

function get_dr_task_by_id {
    param($ms_ip, $fios_session_id, $task_id)
    $endpoint = 'https://' + $ms_ip + ":8443/jss/tasks/" + $task_id
    $header = get_dr_header($fios_session_id)
    $response = get_request $endpoint $header
    return $response
}

function wait_for_dr_task_finish
{
    param($ms_ip, $fios_session_id, $task_id)
    Write-Host "Task id: $task_id"
    $dr_task = get_dr_task_by_id $ms_ip $fios_session_id $task_id
    $task_state = $dr_task.state
    $task_progress = $dr_task.progress
    $task_description = $dr_task.description
    while ($task_progress -ne "100")
    {
        Write-Host "State: $task_state"
    Write-Host "Progress: $task_progress"
        Write-Host "Sleep for 30 seconds and wait for task $task_description to finish"
        start-sleep 30
        $dr_task = get_dr_task_by_id $ms_ip $fios_session_id $task_id
        $task_state = $dr_task.state
    $task_progress = $dr_task.progress
    }
    $err_code = $dr_task.errorCode
    if ($err_code -eq 0) {
        Write-Host "Task $task_description finished successfully: $task_state"
        return 0
    } else {
        write-warning "Task $task_description finished w/ error: $err_code $dr_task.errorMessage"
        return 1
    }
}

function configure_cluster {
    param($ms_ip, $cluster_name, $fios_session_id)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $retry_count=0
    $cluster_params = @()
    $endpoint = 'https://' + $ms_ip + ":8443/jss/clusters/configure"
    $primary_site_dcs = get_site_dc_state $ms_ip $fios_session_id
    foreach ($dc in $primary_site_dcs) {
        foreach ($cluster in $dc.clusters) {
            if ($cluster.clusterName.ToLower() -eq $cluster_name.ToLower()) {
                $params = @{
                    "clusterId" = $cluster.id
                    "packageType" = "DR"
                }
                $cluster_params += $params
            }
        }
    }
    $header = get_dr_header($fios_session_id)
    $body = @{
        "clustersConfigureParams" = $cluster_params
    }
    $json = $body | Convertto-JSON
    do{
        $task = post_request $endpoint $header $json
        # wait for cluster configuration finish before we can remove the elevated privileges
        $status = wait_for_dr_task_finish $ms_ip $fios_session_id $task.id
        $retry_count++
    }while(($retry_count -lt 3) -and ($status -ne 0))
    if (($retry_count -eq 3) -and ($status -ne 0)){
        Write-Error "ERROR: Configure Cluster failed. Please check MS task logs, resolve the issues and run again." -ErrorAction Stop
    }
    return $status
}

function unconfigure_cluster {
    param($ms_ip, $cluster_name, $fios_session_id)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $retry_count=0
    $cluster_params = @()
    $endpoint = 'https://' + $ms_ip + ":8443/jss/clusters/unconfigure"
    $primary_site_dcs = get_site_dc_state $ms_ip $fios_session_id
    $cluster_state=get_cluster_state $ms_ip $fios_session_id $cluster_name
    if ($cluster_state.software.name -eq 'jetdr') {
        Write-Host "Cluster $cluster_name has JetStream iofilter installed. Checking if there are 4 hosts in the custer in order to proceed with uninstall"
        $HostCount=Get-Cluster -Name $cluster_name -ErrorAction SilentlyContinue | Get-VMHost
        if ($HostCount.count -lt 4) {
            Write-Error "Cluster to be unconfigured has less than 4 hosts. Please make sure to have at least 4 hosts in protected cluster" -ErrorAction Stop
        }
        else {
            Write-Host "SUCCESS: Protected cluster satisfies the number of host requirement"
        }
    foreach ($dc in $primary_site_dcs) {
        foreach ($cluster in $dc.clusters) {
            if ($cluster.clusterName.ToLower() -eq $cluster_name.ToLower()) {
                if ($cluster.domainCnt -ne 0) {
                    Write-Error "ERROR: Cannot unconfigure cluster as it is used by a Protected Domain" -ErrorAction Stop
                }
                $params = @{
                    "clusterId" = $cluster.id
                }
                $cluster_params += $params
            }
        }
    }
    $header = get_dr_header($fios_session_id)
    $body = @{
        "clustersUnconfigureParams" = $cluster_params
    }
    $json = $body | Convertto-JSON
    do{
        $task = post_request $endpoint $header $json
        # wait for cluster unconfiguration finish before we can remove the elevated privileges
        $status = wait_for_dr_task_finish $ms_ip $fios_session_id $task.id
        $retry_count++
    }while(($retry_count -lt 3) -and ($status -ne 0))
    if (($retry_count -eq 3) -and ($status -ne 0)){
        Write-Error "ERROR: Unconfigure Cluster failed. Please check MS task logs, resolve the issues and run again." -ErrorAction Stop
    }
    return $status
    }
    else {
        Write-Host "Cluster doesn't have jetdr iofilter installed. Skipping unconfigure cluster"
    }
}

function get_ms_state {
    param($ms_ip, $ms_user, $ms_pwd)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $header = @{
        "Accept" = "application/json"
        'content-type' = 'application/json'
        'ms-session' = login_ms_portal $ms_ip $ms_user $ms_pwd
    }

    $endpoint = 'https://' + $ms_ip + ":8443/jss/ms"
    $response = get_request $endpoint $header
    if (!$response) {
        Write-Host "MSA state api didn't return anything."
    }
    else {
        if($response.configured -eq $False) {
            Write-Host "MSA is not registered to any vCenter server"
        }
        else {
            Write-Host "MSA is registered"
        }
    }
    return $response
}

function get_system_state {
    param($ms_ip, $ms_user, $ms_pwd, $hostname, $dns)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $ms_up = ping_vm $ms_ip 5
    if (!$ms_up) {
        Write-Host "MSA ip $ms_ip not pingable."
        return
    }
    else {
        Write-Host "MSA is pingable."
        get_ms_state $ms_ip $ms_user $ms_pwd
    }
    $rc = ping_vm $dns 5
    if (!$rc) {
        Write-Host "ERROR: Dns server $dns not pingable."
        return
    }
    else {
        Write-Host "Dns server $dns is pingable"
    }
    $check_hostname=test-connection $hostname
    $var=$check_hostname[0].Address
    $var | Out-String | Write-Host
    Write-Host "DNS IP address for your Management Server appliance $hostname is $var"
    if ($check_hostname[0].Address.IPAddressToString -eq $ms_ip) {
        Write-Host "DNS IP address $var resolves to hostname $hostname"
    }
    else{
        Write-Host "ERROR: DNS IP address $var doesn't resolve to hostname $hostname"
    }
}

function get_system_state_install {
    param($msvm_name, $msa_cluster, $protected_cluster, $datastore, $network)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $error=$False
    $rc=Get-Cluster -Name $protected_cluster -ErrorAction SilentlyContinue
    if (!$rc) {
        Write-Error "Specified cluster '$protected_cluster' does not exist in the Datacenter. Please provide correct details."
        $error=$True
    }
    else {
        Write-Host "SUCCESS: Cluster '$protected_cluster' provided for protection exists in the Datacenter"
        $HostCount=Get-Cluster -Name $protected_cluster -ErrorAction SilentlyContinue | Get-VMHost
        if ($HostCount.count -lt 3) {
            Write-Error "Cluster to be protected has less than 3 hosts. Please make sure to have at least 3 hosts in protected cluster"
            $error=$True
        }
        else {
            Write-Host "SUCCESS: Protected cluster satisfies the number of host requirement"
        }
    }
    $rc =  getVasaProvider $protected_cluster
    if ($rc -eq 1) {
        $error=$True
    }
    $rc=Get-Cluster -Name $msa_cluster -ErrorAction SilentlyContinue
    if (!$rc) {
        Write-Error "Specified cluster '$msa_cluster' for deploying MSA does not exist in the Datacenter. Please provide correct details."
        $error=$True
    }
    else {
        Write-Host "SUCCESS: Cluster '$msa_cluster' provided for deploying MSA exists in the Datacenter"
    }

    $rc=Get-VM $msvm_name -ErrorAction SilentlyContinue
    if ($rc) {
        Write-Error "VM with name '$msvm_name' already exists in datacenter. Please remove the VM before proceeding"
        $error=$True
    }
    else{
        Write-Host "SUCCESS: MSA vm doesn't exist in datacenter."
    }

    # Check if provided datastore exists in the datacenter
    $ds_obj=Get-DataStore $datastore -ErrorAction SilentlyContinue
    if ($ds_obj -eq $null) {
        Write-Error "Specified datastore $datastore not found in the datacenter. Please provide a correct datastore name."
        $error = $True
    }
    else {
        $ds_space_gb=$ds_obj.FreeSpaceGB
        if ($ds_space_gb -lt 60) {
            Write-Error "Datastore provided for MS deployment doesn't have enough free space for the deployment. Minimum required 60GB"
            $error = $True
        }
        else {
            Write-Host "SUCCESS: Datastore has enough free space for JetDR ova deployment."
        }
    }

    # Check if provided network exists in the datacenter
    $nw_obj = Get-VirtualNetwork $network -ErrorAction SilentlyContinue
    if ($nw_obj -eq $null) {
        Write-Error "Specified network name $network not found in the datacenter. Please provide a correct network name"
        $error = $True
    }
    else {
        $AVSModule = Get-Module -Name Microsoft.AVS.Management
        $AVSProtectedNWS = & $AVSModule {Get-ProtectedNetworks}
        if ($AVSProtectedNWS.Name.ToLower() -Contains $network.ToLower()) {
            Write-Error "Network $network is one of AVS protected networks. Please provide a different network for MSA deployment"
            $error = $True
        }
        else {
            Write-Host "SUCCESS: Network provided for MSA is not an AVS protected network."
        }
    }

    $extMgr = Get-View ExtensionManager
    $jsdrplugin=$extMgr.ExtensionList | select Key
    if ($jsdrplugin.Key -contains "com.jetstream.primary.jetdrplugin") {
        Write-Error "VCenter already has JetDR plugin. Please unregister vCenter or cleanup any stale plugins before continuing."
        $error=$True
    }
    else {
        Write-Host "SUCCESS: VCenter doesn't have JetDR plugin."
    }

    if ($error -eq $true) {
        Write-Error "There were errors in the preflight check. Please resolve the errors and try again" -ErrorAction Stop
    }
    else {
        Write-Host "SUCCESS: Preflight checks succeeded."
    }
}

function get_system_state_configure {
    param($protected_cluster)
    $rc=Get-Cluster -Name $protected_cluster -ErrorAction SilentlyContinue
    if (!$rc) {
        Write-Error "Specified cluster '$protected_cluster' does not exist in the Datacenter. Please provide correct details."
        $error=$True
    }
    else {
        Write-Host "SUCCESS: Cluster '$protected_cluster' provided for protection exists in the Datacenter"
        $HostCount=Get-Cluster -Name $protected_cluster -ErrorAction SilentlyContinue | Get-VMHost
        if ($HostCount.count -lt 3) {
            Write-Error "Cluster to be protected has less than 3 hosts. Please make sure to have at least 3 hosts in protected cluster"
            $error=$True
        }
        else {
            Write-Host "SUCCESS: Protected cluster satisfies the number of host requirement"
        }
    }
    $rc =  getVasaProvider $protected_cluster
    if ($rc -eq 1) {
        $error=$True
    }

    $extMgr = Get-View ExtensionManager
    $jsdrplugin=$extMgr.ExtensionList | select Key
    if ($jsdrplugin.Key -contains "com.jetstream.primary.jetdrplugin") {
        Write-Host "SUCCESS: VCenter is registered to JetDR MSA."
    }
    else {
        Write-Host "ERROR: VCenter is not registered to any JetDR MSA. Please make sure VCenter is registered before trying configure"
        $error=$True
    }

    if ($error -eq $true) {
        Write-Error "There were errors in the preflight check. Please resolve the errors and try again" -ErrorAction Stop
    }
    else {
        Write-Host "SUCCESS: Preflight checks succeeded."
    }
}

function get_system_state_uninstall {
    param($ms_ip, $protected_cluster, $ms_user, $ms_pwd)
    Write-Host "### $($MyInvocation.MyCommand) ###"
    $error=$False
    $rc=Get-Cluster -Name $protected_cluster -ErrorAction SilentlyContinue
    if (!$rc) {
        Write-Error "Specified cluster '$protected_cluster' does not exist in the Datacenter. Please provide correct details."
        $error=$True
    }
    else {
        Write-Host "SUCCESS: Cluster '$protected_cluster' provided for unconfiguration exists in the Datacenter"
    }

    $ms_up=ms_rest_state $ms_ip $ms_user $ms_pwd 60
    if ($ms_up) {
        Write-Host "JetStream MSA is up and running"
        $response=get_ms_state $ms_ip $ms_user $ms_pwd
        if($response.configured -eq $True) {
            Write-Host "SUCCESS: VCenter is registered to MSA $ms_ip."
        }
        else {
            Write-Error "VCenter is not registered to any MSA. Cannot proceed with uninstall"
            $error=$true
        }
    }
    else {
        Write-Error "JetStream MSA is not accessible. Please make sure it is running and try Uninstall again"
        $error=$true
    }
    if ($error -eq $true) {
        Write-Error "There were errors in the preflight check. Please resolve the errors and try again" -ErrorAction Stop
    }
    else {
        Write-Host "SUCCESS: Preflight check succeeded. Proceeding with uninstallation."
    }
}

function restart_cim {
    param($cluster)
    Write-Host "### $($MyInvocation.MyCommand) ###"

    $hosts=Get-Cluster $cluster | Get-VMHost
    foreach ($ESXhost in $hosts){
        Get-VMHost -name $ESXhost | Get-VMHostService | where {$_.key -eq 'sfcbd-watchdog'} | Stop-VMHostService -Confirm:$false
        Get-VMHost -name $ESXhost | Get-VMHostService | where {$_.key -eq 'sfcbd-watchdog'} | Start-VMHostService -Confirm:$false
    }
}

function checkPSVersion {
    # This function checks whether Powershell version in the system is greatear than 7 or not
    $PowerShellVersion = $PSVersionTable.PSVersion.Major
    if($PowerShellVersion -lt 7) {
        write-error "Powershell version is less than 7. Please install powershell version 7.x and run again." -ErrorAction Stop
    }
    else {
        Write-Host "SUCCESS: Check for powershell version passed"
    }
}

function checkRole {
    # Checks for the presence of a role in the vCenter
    param(
          [String]$role
    )
    $role_exists = Get-VIRole | Where-Object {$_.Name -eq $role}
    if ($role_exists) {
        Write-Host "SUCCESS: Check for $role role in vCenter passed"
    }
    else {
        write-error "$role role does not exist in the VCenter. Please make sure it exists before we can proceed with the configuration" -ErrorAction Stop
    }
}

function checkModule {
    param(
          [String]$module
    )

    $module_exists=Get-Module -Name $module
    if($module_exists) {
        Write-Host "SUCCESS: Check for module $module passed"
    }
    else {
        write-error "Module $module not available. Please make sure the module is imported and try again." -ErrorAction Stop
    }
}

function checkVcaddress {
    if (-Not $VC_ADDRESS) {
        Write-Error "VC_ADDRESS is not set as a powershell variable. Please set it and run the script again" -ErrorAction Stop
    }
    else {
        Write-Host "SUCCESS: VCenter Address is set."
    }
}

function getVasaProvider {
    param(
          [String]$cluster
    )
    $vasacount=0
    $error=0
    $vasaprovider = Get-VasaProvider -Server $VC_ADDRESS
    $hosts=Get-Cluster -Name $cluster -ErrorAction SilentlyContinue | Get-VMHost
    $hostcount = $hosts.count

    # Checking if storage provider is available for each host
    ForEach ($vasa in $vasaprovider) {
        ForEach ($hostname in $hosts.name) {
            if ($vasa.name.split() -Contains $hostname -And $vasa.namespace -eq "iofilters") {
                $vasacount++
                if ($vasa.status -eq "online") {
                    write-host "SUCCESS: Storage Provider for $hostname is Online"
                }
                else {
                    write-error "Iofilter Storage Provider for $hostname is not Online. Please synchronize storage providers and make sure they are online before trying again"
                    $error=1
                }
            }
        }
    }
    if ($vasacount -ne $hostcount){
        write-error "Iofilter Storage providers for all hosts in the cluster to be protected is not available. Please synchronize and make sure they are present and online before trying again."
        $error=1
    }
    return $error
}