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. Deleting before downloading again." Remove-Item $download_file_path } Write-Host "### $($MyInvocation.MyCommand) ###" $start_time = Get-Date Invoke-WebRequest $url -OutFile $download_file_path 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 } Expand-Archive -Path $zip_filename -DestinationPath $destination_folder if (!$?) { write-error "Unzip jetdr bundle failed, rc=$?" -ErrorAction Stop } } 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) ###" #$ovf_path = "e:\tmp\jetstreamsoft-jetdr-2.8.0.2793.ova" $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 Import-VApp -Source $ovf_path -OvfConfiguration $ovfConfig -Name $vm_name -vmHost $vmhost -Location $cluster -Datastore $datastore -DiskStorageFormat $disk_format -Confirm:$false > $null if (!$?) { 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=ping_vm $vm_ip 600 if (!$rc) { write-error "Failed to ping $vm_ip after 600 seconds" -ErrorAction Stop } Write-Host "Sleep for 2 more minute after Manamgement server becomes pingable" 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" -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_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" $primary_site_registered_vc | Out-String | Write-Verbose 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) { $dc.clusters 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" $cluster_state | Out-String | Write-Verbose 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 #while (($task_state -ne "Task successfully completed") -and ($task_progress -ne 100)) 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_id 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_id finished successfully: $task_state" return 0 } else { write-warning "Task $task_id 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) { $dc.clusters 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) $cluster_params | Out-String | Write-Verbose $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 foreach ($dc in $primary_site_dcs) { $dc.clusters foreach ($cluster in $dc.clusters) { if ($cluster.clusterName.ToLower() -eq $cluster_name.ToLower()) { $params = @{ "clusterId" = $cluster.id } $cluster_params += $params } } } $header = get_dr_header($fios_session_id) $cluster_params | Out-String | Write-Verbose $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 } 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 } $header | Out-String | Write-Host $endpoint = 'https://' + $ms_ip + ":8443/jss/ms" $response = get_request $endpoint $header 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) 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 4) { Write-Error "Cluster to be protected has less than 4 hosts. Please make sure to have at least 4 hosts in protected cluster" $error=$True } else { Write-Host "SUCCESS: Protected cluster satisfies the number of host requirement" } } $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." } $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_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" $HostCount=Get-Cluster -Name $protected_cluster -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" $error=$True } else { Write-Host "SUCCESS: Protected cluster satisfies the number of host requirement" } } $ms_up = ping_vm $ms_ip 5 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." } } |