NetApp.CBS.AVS.psm1

using module Microsoft.AVS.Management

<#
Copyright 2021 Netapp, Inc.
#>


#
# Script module for module 'NetApp.SC'
#
Set-StrictMode -Version Latest

<#
.SYNOPSIS
Installs Cloud Backup for Virtual Machines to protect Virtual Machines and datastores based on Azure NetApp Files.
 
.DESCRIPTION
Installs Cloud Backup for Virtual Machines to protect Virtual Machines and datastores based on Azure NetApp Files.
 
.PARAMETER MaintenanceUserPassword
Password for the appliance maintenance user. This is used for performing maintenance tasks in the appliance.
 
.PARAMETER AppliancePassword
Password for the appliance user account. This is used for authenticating with the API endpoints that are exposed by the appliance.
 
.PARAMETER ApplianceUser
Appliance user account is used for authenticating with the API endpoints that are exposed by the appliance.
 
.PARAMETER PrimaryDNS
Primary DNS server IP address.
 
.PARAMETER Gateway
Gateway IP address.
 
.PARAMETER Netmask
Subnet mask.
 
.PARAMETER ApplianceIPAddress
IPV4 address to be used for the appliance.
 
.PARAMETER ApplianceNetworkName
Network name to be used for the appliance.
 
.PARAMETER NetworkMapping
Destination network to be used for the appliance.
 
.PARAMETER VmDatastore
Datastore to be used for the appliance.
 
.PARAMETER EsxiCluster
Destination ESXi cluster name to be used for deploying the appliance.
 
.PARAMETER ApplianceVirtualMachineName
Virtual Machine name for the appliance.
 
.EXAMPLE
Install-NetAppCBSAppliance -AcceptNetAppEulaAggrement $true -ApplianceVirtualMachineName "VM1" -EsxiCluster "cluster1" -VmDatastore "datastore1" -NetworkMapping "VM Network" -ApplianceNetworkName "scvova" -ApplianceIPAddress "19.132.15.124" -Netmask "255.255.255.0" -Gateway "11.252.1.1" -PrimaryDNS "10.232.1.60"
 
Description
---------------------------------------
Installs Cloud Backup for Virtual Machines with a static IP address for protection of Virtual Machines and datastores based on Azure NetApp Files.
#>


function Install-NetAppCBSAppliance {
  [AVSAttribute(60, UpdatesSDDC = $True)]
  [CmdletBinding()]
  param(   

    [Parameter(
      Mandatory = $true,
      Position = 12,
      HelpMessage = 'Password for the appliance maintenance user. This is used for performing maintenance tasks in the appliance.')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $MaintenanceUserPassword,
    
    [Parameter(
      Mandatory = $true,
      Position = 11,
      HelpMessage = 'Password for the appliance user account. This is used for authenticating with the API endpoints that are exposed by the appliance.')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword,
  
    [Parameter(
      Mandatory = $true,
      Position = 10,
      HelpMessage = 'Appliance user account is used for authenticating with the API endpoints that are exposed by the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,
    
    [Parameter(
      Mandatory = $true,
      Position = 9,
      HelpMessage = 'Primary DNS server IP address.')]
    [string]
    $PrimaryDNS,
  
    [Parameter(
      Mandatory = $true,
      Position = 8,
      HelpMessage = 'Gateway IP address.')]
    [string]
    $Gateway,
  
    [Parameter(
      Mandatory = $true,
      Position = 7,
      HelpMessage = 'Subnet mask.')]
    [string]
    $Netmask,
  
    [Parameter(
      Mandatory = $true,
      Position = 6,
      HelpMessage = 'IPV4 address to be used for the appliance.')]
    [string]
    $ApplianceIPAddress,
  
    [Parameter(
      Mandatory = $true,
      Position = 5,
      HelpMessage = 'Network name to be used for the appliance.')]
    [string]
    $ApplianceNetworkName,
  
    [Parameter(
      Mandatory = $true,
      Position = 4,
      HelpMessage = 'Destination network to be used for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $NetworkMapping,

    [Parameter(
      Mandatory = $true,
      Position = 3,
      HelpMessage = 'Datastore to be used for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $VmDatastore,

    [Parameter(
      Mandatory = $true,
      Position = 2,
      HelpMessage = 'Destination ESXi cluster name to be used for deploying the appliance.')]
    [string]
    $EsxiCluster,

    [Parameter(
      Mandatory = $true,
      Position = 1,
      HelpMessage = 'Virtual Machine name for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceVirtualMachineName,

    [Parameter(
      Mandatory = $true,
      Position = 0,
      HelpMessage = "Accept the End User License Agrement 'https://www.netapp.com/pdf.html?item=/media/14114-enduserlicenseagreementworldwide.pdf'")]
    [ValidateNotNullOrEmpty()]
    [boolean]
    $AcceptNetAppEulaAggrement
  )
  
  $ApplianceIPAddress = $ApplianceIPAddress.Trim()
  $Netmask = $Netmask.Trim()
  $Gateway = $Gateway.Trim()
  $PrimaryDNS = $PrimaryDNS.Trim()
  $ApplianceUser = $ApplianceUser.Trim()  
    
  $scvcredential = New-Object System.Management.Automation.PSCredential ($ApplianceUser, $AppliancePassword)
  $maintusercredential = New-Object System.Management.Automation.PSCredential ("maint", $MaintenanceUserPassword)
  
  if ($EsxiCluster -eq $null -or $EsxiCluster -eq '') {
    Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -ApplianceIPAddress  $ApplianceIPAddress -Netmask $Netmask -Gateway $Gateway -PrimaryDNS $PrimaryDNS  -scvcredential $scvcredential -maintusercredential $maintusercredential
  }
  else {
    Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -EsxiCluster $EsxiCluster -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -ApplianceIPAddress  $ApplianceIPAddress -Netmask $Netmask -Gateway $Gateway -PrimaryDNS $PrimaryDNS  -scvcredential $scvcredential -maintusercredential $maintusercredential
  }
}

<#
.SYNOPSIS
Installs Cloud Backup for Virtual Machines to protect Virtual Machines and datastores based on Azure NetApp Files.
 
.DESCRIPTION
Installs Cloud Backup for Virtual Machines to protect Virtual Machines and datastores based on Azure NetApp Files.
 
.PARAMETER MaintenanceUserPassword
Password for the appliance maintenance user. This is used for performing maintenance tasks in the appliance.
 
.PARAMETER AppliancePassword
Password for the appliance user account. This is used for authenticating with the API endpoints that are exposed by the appliance.
 
.PARAMETER ApplianceUser
Appliance user account is used for authenticating with the API endpoints that are exposed by the appliance.
 
.PARAMETER ApplianceNetworkName
Network name to be used for the appliance.
 
.PARAMETER NetworkMapping
Destination network to be used for the appliance.
 
.PARAMETER VmDatastore
Datastore to be used for the appliance.
 
.PARAMETER EsxiCluster
Destination ESXi cluster name to be used for deploying the appliance.
 
.PARAMETER ApplianceVirtualMachineName
Virtual Machine name for the appliance.
 
.EXAMPLE
Install-NetAppCBSApplianceUsingDHCP -AcceptNetAppEulaAggrement $true -ApplianceVirtualMachineName "VM1" -EsxiCluster "cluster1" -VmDatastore "datastore1" -NetworkMapping "VM Network" -ApplianceNetworkName "scvova"
 
Description
---------------------------------------
Installs the OVA file using dynamic IP address.
#>


function Install-NetAppCBSApplianceUsingDHCP {
  [AVSAttribute(60, UpdatesSDDC = $True)]
  [CmdletBinding()]
  param(  
   
    [Parameter(
      Mandatory = $true,
      Position = 8,
      HelpMessage = 'Password for the appliance maintenance user. This is used for performing maintenance tasks in the appliance.')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $MaintenanceUserPassword,
   
    [Parameter(
      Mandatory = $true,
      Position = 7,
      HelpMessage = 'Password for the appliance user account. This is used for authenticating with the API endpoints that are exposed by the appliance.')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword,

    [Parameter(
      Mandatory = $true,
      Position = 6,
      HelpMessage = 'Appliance user account is used for authenticating with the API endpoints that are exposed by the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,

    [Parameter(
      Mandatory = $true,
      Position = 5,
      HelpMessage = 'Network name to be used for the appliance.')]
    [string]  
    $ApplianceNetworkName,
  
    [Parameter(
      Mandatory = $true,
      Position = 4,
      HelpMessage = 'Destination network to be used for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $NetworkMapping,
    
    [Parameter(
      Mandatory = $true,
      Position = 3,
      HelpMessage = 'Datastore to be used for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $VmDatastore,

    [Parameter(
      Mandatory = $true,
      Position = 2,
      HelpMessage = 'Destination ESXi cluster name to be used for deploying the appliance.')]
    [string]
    $EsxiCluster,
    
    [Parameter(
      Mandatory = $true,
      Position = 1,
      HelpMessage = 'Virtual Machine name for the appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceVirtualMachineName,

    [Parameter(
      Mandatory = $true,
      Position = 0,
      HelpMessage = "Accept End User License Agrement 'https://www.netapp.com/pdf.html?item=/media/14114-enduserlicenseagreementworldwide.pdf'")]
    [ValidateNotNullOrEmpty()]
    [boolean]
    $AcceptNetAppEulaAggrement
  )
    
  $scvcredential = New-Object System.Management.Automation.PSCredential ($ApplianceUser, $AppliancePassword)
  $maintusercredential = New-Object System.Management.Automation.PSCredential ("maint", $MaintenanceUserPassword)
  if ($EsxiCluster -eq $null -or $EsxiCluster -eq '') {
    Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -Dhcp $true -scvcredential $scvcredential -maintusercredential $maintusercredential
  }
  else {
    Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -EsxiCluster $EsxiCluster -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -Dhcp $true -scvcredential $scvcredential -maintusercredential $maintusercredential
  }
}

<#
.SYNOPSIS
Upgrades Cloud Backup for Virtual Machines for protection of Virtual Machines and datastores based on Azure NetApp Files.
 
.DESCRIPTION
Upgrades Cloud Backup for Virtual Machines for protection of Virtual Machines and datastores based on Azure NetApp Files.
 
.PARAMETER AppliancePassword
Password of the user hosting API services in the appliance.
 
.PARAMETER ApplianceUser
User Account for hosting API services in the appliance
 
.EXAMPLE
Update-NetAppCBSAppliance
 
Description
---------------------------------------
Upgrades the appliance
#>


function Invoke-UpgradeNetAppCBSAppliance { 
  [AVSAttribute(60, UpdatesSDDC = $True)]
  [CmdletBinding()]
  param(    
       
    [Parameter(
      Mandatory = $true,
      Position = 2,
      HelpMessage = 'Appliance password')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword,
    
    [Parameter(
      Mandatory = $true,
      Position = 1,
      HelpMessage = 'Appliance user name.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,

    [Parameter(
      Mandatory = $true,
      HelpMessage = 'Upgrade operation will restart NetApp CBS Appliance. Click Accept to upgrade.')]
    [bool]
    $AreYouSureToUpgrade
  )

  if ($AreYouSureToUpgrade -eq $True) {
    $scvcredential = New-Object System.Management.Automation.PSCredential ($ApplianceUser, $AppliancePassword)
    update-netapp-cbs-appliance -scvcredential  $scvcredential
  }
  else {
    write-error -Message "You must enable 'AreYouSureToUpgrade' to upgrade." -ErrorAction Stop 
  }  
}

<#
.SYNOPSIS
Resets the vCenter account used internally by the appliance.
 
.DESCRIPTION
Resets the vCenter account used internally by the appliance.
 
.EXAMPLE
Invoke-ResetNetAppCBSApplianceVCenterPassword
 
Description
---------------------------------------
Resets the vCenter account used internally by the appliance.
 
#>

function Invoke-ResetNetAppCBSApplianceVCenterPassword {
  <#
   High level task of this cmdlets
   1. Check if tag AVS_ANF_CLOUD_ADMIN_VM_TAG exists.
   2. If the tag does not exist, throw error.
   3. If tag exist, get teh VM detail.
   4. Check if VM assosicated with Tag present.
   5. If VM not present, throw eror
   6. IF VM present
      A. Change password for teh user 'NetAppSCDPAdmin'
      B. Update vApp options for the users new password.
  #>


     [CmdletBinding()]
  param(        
    [Parameter(
      Mandatory = $true,
      HelpMessage = 'Resetting the password will restart NetApp CBS Appliance. Click Accept to reset the password.')]
    [bool]
    $AreYouSureToResetPassword
  )
  
  if ($AreYouSureToResetPassword -eq $True) {
    set-netapp-cbs-appliance-password
  }
  else {
    write-error -Message "You must enable 'AreYouSureToResetPassword' to reset the password." -ErrorAction Stop 
  }
  
}

<#
.SYNOPSIS
Uninstalls the virtual appliance.
 
.DESCRIPTION
Uninstalls the virtual appliance.
 
.EXAMPLE
UnInstall-NetAppCBSAppliance
 
Description
---------------------------------------
Uninstalls the virtual appliance
 
#>


function UnInstall-NetAppCBSAppliance {
    
     [CmdletBinding()]
  param(        
    [Parameter(
      Mandatory = $true,
      HelpMessage = 'Accept to uninstall the virtual appliance.')]
    [bool]
    $AreYouSureToDelete
  )
  
  if ($AreYouSureToDelete -eq $True) {
    uninstall-netapp-cbs-appliance
  }
  else {
    write-error -Message "You must enable 'AreYouSureDelete' to uninstall the virtual appliance." -ErrorAction Stop 
  }
}

# Internal helper functions
function Show-Progress {

  param(
    [Parameter(Mandatory)]
    [int]
    $TotalNoOfTimes,

    [Parameter(Mandatory)]
    [int]
    $SleepTime,

    [Parameter(Mandatory)]
    [string]
    $ActivityText,

    [Parameter(Mandatory)]
    [string]
    $ProgressText,

    [Parameter()]
    [boolean]
    $HidePercentageComplete
  )

  $TotalNoOfTimes = $TotalNoOfTimes + 1
  $i = 0
  for ($i = 0; $i -lt $TotalNoOfTimes; $i++) {
    $percentComplete = ($i / $TotalNoOfTimes) * 100
    if ($HidePercentageComplete -eq $true) {
      Write-DebugLog -Message "-Activity $ActivityText -Status $ProgressText"
    }
    else {
      Write-DebugLog -Message "-Activity $ActivityText -Status $ProgressText : $percentComplete"
    }
    Start-Sleep $SleepTime
  }
  
  Write-DebugLog -Message "-Activity $ActivityText -Status $ProgressText : $percentComplete"
}
function Restart-VMwareVM {
  
  <#
 
.DESCRIPTION
This fucntion restarts the Virtual Machine.
 
.PARAMETER ApplianceVirtualMachine
Specify the Virtual Machine for the virtual appliance.
 
.EXAMPLE
Restart-VMwareVM
 
Description
---------------------------------------
Restart Virtual Machine
 
#>

  param(
    [Parameter(Mandatory)]
    [string]
    $ApplianceVirtualMachine
  )
  
  $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine -ErrorAction SilentlyContinue -ErrorVariable GetVMError
  
  if ($GetVMError) {
    Write-DebugLog $GetVMError | Out-String
  }
  
  if ($VMGuestObject) {
      
    if ($VMGuestObject.State -eq "Running") {            
          
      try {
        Shutdown-VMGuest -VM $ApplianceVirtualMachine -Confirm:$false -ErrorAction Stop | Out-Null
        Start-Sleep 5
        $Count = 10
        For ($i = 0; $i -le $Count; $i++) {
            
          $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine
            
          if ($VMGuestObject.State -eq "NotRunning") {
            break
          }
          else {
            Start-Sleep 10
          }
          $PercentComplete = ($i / $Count) * 100
             
          Write-DebugLog -Message "Shutdown-VMGuest -Status $i/$count : $PercentComplete % "
        }
        
        Write-DebugLog -Message "Shutdown-VMGuest -Status Done -complete"
        
        Start-VM -VM $ApplianceVirtualMachine | Wait-Tools | Out-Null
        $Count = 10
        For ($i = 0; $i -le $Count; $i++) {
            
          $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine
            
          if ($VMGuestObject.State -eq "Running") {
            break
          }
          else {
            Start-Sleep 10
          }
          $PercentComplete = ($i / $Count) * 100
          Write-DebugLog -Message "Start-VM -Status $i/$count : $PercentComplete"
        }
        Write-DebugLog -Message "Start-VM -Status Done -complete"
        
      }
      catch {
        Write-DebugLog -Message  $_.exception
        Write-Error -ErrorId "MOUNT_FAILED" -Message $_.exception.Message
      } 
          
    }
    else {
          
      Start-VM -VM $ApplianceVirtualMachine | Wait-Tools | Out-Null
      $Count = 10
      For ($i = 0; $i -le $Count; $i++) {
            
        $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine
            
        if ($VMGuestObject.State -eq "Running") {
          break
        }
        else {
          Start-Sleep 10
        }
        $PercentComplete = ($i / $Count) * 100
        Write-DebugLog -Message "Start-VM -Status $i/$count : $PercentComplete % Complete -PercentComplete $PercentComplete"
      }
      Write-DebugLog -Message "Start-VM -Status Done -complete"
        
    }
  }     

  Write-DebugLog -Message "Restart vm successfull"
  Write-Verbose -Message "Restart vm successfull"
}

function Restart-VMwareVMGuest {
  <#
   
  .DESCRIPTION
  This fucntion restarts the Virtual Machine.
   
  .PARAMETER ApplianceVirtualMachine
  Specify the Virtual Machine for the virtual appliance.
   
  .EXAMPLE
  Restart-VMwareVMGuest
   
  Description
  ---------------------------------------
  Restart Virtual Machine
   
  #>

  param(
    [Parameter(Mandatory)]
    [string]
    $ApplianceVirtualMachine
  )
  
  $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine -ErrorAction SilentlyContinue -ErrorVariable GetVMError
  
  if ($GetVMError) {
    Write-DebugLog $GetVMError | Out-String
  }
  
  if ($VMGuestObject) {
      
    if ($VMGuestObject.State -eq "Running") {            
      
      try {
        Restart-VMGuest -VM $ApplianceVirtualMachine -Confirm:$false | Wait-Tools -ErrorAction Stop | Out-Null 
        Start-Sleep 30
        $Count = 10
        For ($i = 0; $i -le $Count; $i++) {
      
          $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine
      
          if ($VMGuestObject.State -eq "Running") {
            break
          }
          else {
            Start-Sleep 30
          }
          $PercentComplete = ($i / $Count) * 100
       
          Write-DebugLog -Message "Restart-VMGuest -Status $i/$count : $PercentComplete % "
        }
    
        Write-DebugLog -Message "Restart-VMGuest -Status Done -complete"
    
      }
      catch {
        Write-DebugLog -Message  $_.exception
        Write-Error -ErrorId "MOUNT_FAILED" -Message $_.exception.Message
      } 
      
    }
    else {
      
      Start-VM -VM $ApplianceVirtualMachine | Wait-Tools | Out-Null
      Start-Sleep 30
      $Count = 10
      For ($i = 0; $i -le $Count; $i++) {
      
        $VMGuestObject = Get-VMGuest -VM $ApplianceVirtualMachine
      
        if ($VMGuestObject.State -eq "Running") {
          break
        }
        else {
          Start-Sleep 30
        }
        $PercentComplete = ($i / $Count) * 100
        Write-DebugLog -Message "Start-VM -Status $i/$count : $PercentComplete % Complete -PercentComplete $PercentComplete"
      }
      Write-DebugLog -Message "Start-VM -Status Done -complete"
    
    }
  }     
  
  Write-DebugLog -Message "Restart vm successfull"
  Write-Verbose -Message "Restart vm successfull"
}

function PowerOffOnVM {
  param(
    [Parameter()]
    [string]
    $ApplianceVirtualMachine
  )
  Write-DebugLog -Message "Get-VM -Name $ApplianceVirtualMachine -ErrorAction SilentlyContinue -ErrorVariable SCDPRoleError"
  $VMObject = Get-VM -Name $ApplianceVirtualMachine -ErrorAction SilentlyContinue -ErrorVariable GetVMError
  
  if ($GetVMError) {
    Write-DebugLog $GetVMError | Out-String
  }
  
  if ($VMObject) {
    $VMPowerState = $VMObject.PowerState
    Write-DebugLog -Message "VMPowerState: $VMPowerState"
    if ($VMPowerState -ne "PoweredOn") {
      Write-DebugLog -Message "Initiated start operation on the Virtual Machine ."
      Start-VM -VM $ApplianceVirtualMachine | Wait-Tools | Out-Null
      $Count = 10
      For ($i = 0; $i -le $Count; $i++) {
        
        $VMObject = Get-VM -Name $ApplianceVirtualMachine
        $VMPowerState = $VMObject.PowerState
        if ($VMPowerState -eq "PoweredOn") {
          break
        }
        else {
          Start-Sleep 30
        }
      }
      Write-DebugLog -Activity "The Virtual Machine has started. "
    }
    else {
      Write-DebugLog -Message "Stopping virtual machine appliance..."
      $Task = Stop-VM -VM $ApplianceVirtualMachine -Confirm:$false
      Start-Sleep 60
      Write-DebugLog -Message "Stopping virtual machine."
      $StopCount = 10
      :verifyvmpowerstatus For ($stopcounter = 0; $stopcounter -le $StopCount; $stopcounter++) {
        if ($Task.PowerState -eq "PoweredOff") {            
          Write-DebugLog -Message "Virtual machine is powered off."
          Write-DebugLog -Message "Initiated start operation on the Virtual Machine ."
          Start-VM -VM $ApplianceVirtualMachine | Wait-Tools | Out-Null
          $Count = 10
          For ($i = 0; $i -le $Count; $i++) {
            
            $VMObject = Get-VM -Name $ApplianceVirtualMachine
            $VMPowerState = $VMObject.PowerState
            if ($VMPowerState -eq "PoweredOn") {
              Write-DebugLog -Message "Virtual machine is Powered on. "
              break 'verifyvmpowerstatus'
            }
            else {
              Start-Sleep 30
            }
          }
          Write-DebugLog -Activity "The Virtual Machine has started. "
        }
        else {
          Start-Sleep 30
        }
      }
    }
  }
}
  
function Get-BlobContent {

  <#
 
.DESCRIPTION
This function downloads file from Azure account.
 
.PARAMETER BlobName
File name to be downladed.
 
.PARAMETER ContainerName
The Azure storage account container name used for downloading the virtual appliance
 
.PARAMETER StorageAccountName
The Azure storage account name used for downloading the virtual appliance. Contact NetApp for the account name.
 
.EXAMPLE
Get-BlobContent -BlobName 'test.ova' -StorageAccountName 'acountname' -ContainerName 'container'
 
Description
---------------------------------------
Downloads file from Azure account.
 
#>


  param(
    [Parameter(Mandatory)]
    [string]
    $BlobName,

    [Parameter(Mandatory)]
    [string]
    $StorageAccountName,

    [Parameter(Mandatory)]
    [string]
    $ContainerName,
    
    [Parameter(Mandatory)]
    [Long]
    $TotalBlobSizeInKB
  )

  Write-DebugLog -Message " Get-BlobContent execution is started...."
  Write-DebugLog -Message "Downloading file $BlobName"
  $FileUrl = 'https://' + $StorageAccountName + '.blob.core.windows.net/' + $ContainerName + '/' + $BlobName

  try {
    
    $GetLocation = Get-Location
    Write-DebugLog -Message "Downloading file to location $GetLocation"
    $downloadtask = Write-Output (New-Object System.Net.WebClient).DownloadFileTaskAsync($FileUrl, "$GetLocation/$BlobName")
    Write-DebugLog -Message "After DownloadFileTaskAsync called. "
    $destination = "$GetLocation/$BlobName"
        
       $i = 0
    for ($i = 0; $i -lt 60; $i++) {            
      if ($downloadtask.IsCompleted -eq $True) {
        if ($downloadtask.IsCompletedSuccessfully -eq $True) {
          Write-DebugLog  "Percentage Downloaded: 100 %"
          Write-DebugLog -Message "The file $BlobName downloaded."
          Write-DebugLog "Downloaded file saved in the location : $GetLocation"                  
        }
        else {                
          $exception = $downloadtask.Exception
          if ($null -ne $exception -and $null -ne $exception.message) {
            write-error -Message $downloadtask.Exception.message -ErrorAction Stop
          }
          else {
            if ($downloadtask.Status -eq "Faulted") {
              write-error -Message "Could not download the requested file." -ErrorAction Stop
            }
          }        
        }
        break
      }
      else {
        Start-Sleep -Seconds 60            
        $fileSize = ((Get-Item $destination).length / 1KB)            
        $sizeDownloadedPercentage = ($fileSize / $TotalBlobSizeInKB) * 100
        Write-DebugLog  "Percentage Downloaded: $sizeDownloadedPercentage %"
      }
    }
  }
  catch {
    Write-DebugLog -Message $_.exception.Message
    write-error -Message $_.exception.Message -ErrorAction Stop
  }
  
  Write-DebugLog -Message " Get-BlobContent execution is completed."
}

function Get-TimeStamp {
  return "{0:M/d/yyyy} {0:h:mm:ss tt}" -f (Get-Date)
}

function Write-DebugLog {
  param(
    $Message
  )

  Write-Host -Message "$(Get-TimeStamp) $Message"
}

function Get-RandomCharacters ($length, $characters) {
  $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
  $private:ofs = ""
  return [string]$characters[$random]
}

function getSuffledString ([string]$inputString) {
  Write-DebugLog -Message "getSuffledString execution is started."
  $characterArray = $inputString.ToCharArray()
  $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.length
  $outputString = -join $scrambledStringArray
  Write-DebugLog -Message "getSuffledString execution is completed"
  return $outputString
}

function changeMaxRepeatingChar ($pwdText, $maxCharAllowed, $AllChars) {
  Write-DebugLog -Message "changeMaxRepeatingChar execution is started"

  $len = $pwdText.length;

  for ($i = 0; $i -lt $len; $i++) {
    $cur_count = 1;
    for ($j = $i + 1; $j -lt $len; $j++) {
      if ($pwdText[$i] -ne $pwdText[$j]) {
        break
      }
      $cur_count = $cur_count + 1;
      if ($cur_count -gt $maxCharAllowed) {
        $allcharExpectRepeated = $AllChars -replace $pwdText[$j]
        $pwdText = ($pwdText.substring(0, $j)) + (Get-RandomCharacters -length 1 -characters $allcharExpectRepeated) + $pwdText.substring(($j + 1))
        break
      }
    }
  }  
  Write-DebugLog -Message "changeMaxRepeatingChar execution is completed"  
  return $pwdText
}

function Get-Password () {
  <#
.DESCRIPTION
This function generates a new password based on password policy for the appliance user
 
.EXAMPLE
Get-Password
 
Description
---------------------------------------
Returns password
#>


  Write-DebugLog -Message "Get-Password execution is started"
  $password = ""
  #$SpecialChars ='!"ยง$%&/()=?}][{@#*+'
  #Password wont work properly when few special characters present.
  #Thats why few charactors are excluded.
  $SpecialChars = '!%()=}][{@#'
  $UppercaseChars = 'ABCDEFGHKLMNOPRSTUVWXYZ'
  $LowerCaseChars = 'abcdefghiklmnoprstuvwxyz'
  $Numerics = '1234567890'
  $AllAlphaBets = $UppercaseChars + $LowerCaseChars
  $AllChars = $AllAlphaBets + $Numerics

  $SsoPasswordPolicy = Get-SsoPasswordPolicy
     
  $MinNumericCount = $SsoPasswordPolicy | ForEach-Object { $_.MinNumericCount }
  $password += Get-RandomCharacters -length $MinNumericCount -characters $Numerics

  $MinSpecialCharCount = $SsoPasswordPolicy | ForEach-Object { $_.MinSpecialCharCount }
  $password += Get-RandomCharacters -length $MinSpecialCharCount -characters $SpecialChars

  $MinUppercaseCount = $SsoPasswordPolicy | ForEach-Object { $_.MinUppercaseCount }
  $password += Get-RandomCharacters -length $MinUppercaseCount -characters $UppercaseChars

  $MinLowercaseCount = $SsoPasswordPolicy | ForEach-Object { $_.MinLowercaseCount }
  $password += Get-RandomCharacters -length $MinLowercaseCount -characters $LowerCaseChars

  $MinAlphabeticCount = $SsoPasswordPolicy | ForEach-Object { $_.MinAlphabeticCount }
  $ExtraAlphaRequired = $MinAlphabeticCount - ($MinLowercaseCount + $MinUppercaseCount)

  if ($ExtraAlphaRequired -gt 0) {
    $password += Get-RandomCharacters -length $ExtraAlphaRequired -characters $AllAlphaBets
  }

  $MinLength = $SsoPasswordPolicy | ForEach-Object { $_.MinLength }
  $ExtraCharRequiredForMinLength = $MinLength - $password.length

  if ($ExtraCharRequiredForMinLength -gt 0) {
    $password += Get-RandomCharacters -length $ExtraCharRequiredForMinLength -characters ($AllAlphaBets + $Numerics + $SpecialChars)
  }

  $MaxIdenticalAdjacentCharacters = $SsoPasswordPolicy | ForEach-Object { $_.MaxIdenticalAdjacentCharacters }

  $pwdText = changeMaxRepeatingChar -pwdText $password -maxCharAllowed $MaxIdenticalAdjacentCharacters -AllChars $AllChars

  $pwdText = getSuffledString $pwdText
  Write-DebugLog -Message "Get-Password execution is completed"
  return $pwdText
}

function Set-Password {
  <#
 
.DESCRIPTION
This function removes the role from the vCenter.
 
.PARAMETER vcUserCre
Specify the credential.
 
.EXAMPLE
Set-Password
 
Description
---------------------------------------
Removes the role 'role1'
 
#>


  param(
    [Parameter(
      Mandatory = $true,
      HelpMessage = 'Specify the credential.')]
    [System.Management.Automation.PSCredential]
    $vcUserCredential
  )
  
  Write-DebugLog -Message "Set-Password execution is started"
  
  $domain = "vsphere.local"
  $userName = $vcUserCredential.UserName
  $newPassword = $vcUserCredential.GetNetworkCredential().Password
  
  Write-DebugLog -Message "Get-SsoPersonUser -Domain $domain -Name $userName"
  $User = Get-SsoPersonUser -Domain $domain -Name $userName
  
  Write-DebugLog -Message "SCDPAdminObject"

  if ($null -eq $User) {
    Write-DebugLog -Message "No such user"
    write-error "Appliance user with name NetAppSCDPAdmin does not exist" -ErrorAction Stop
  }
  else {  
    #Change NetAppSCDPAdmin password
    try {
      Set-SsoPersonUser -User $User -NewPassword $newPassword -ErrorAction Stop | Out-Null      
      Write-DebugLog -Message "Password changed"
    }
    catch {
      Write-DebugLog -Message "Password change failed"
      write-error "Password change failed" -ErrorAction Stop
    }   
  } 
  Write-DebugLog -Message "Set-Password execution is completed"
}

function Get-ApplianceVirtualMachineUsingTag {

  Write-DebugLog -Message " Get-ApplianceVirtualMachineUsingTag execution started"

  $tagName = "AVS_ANF_CLOUD_ADMIN_VM_TAG"
  $tagCategoryName = "AVS_ANF_CLOUD_ADMIN_VM_TAG_CATEGORY"
  $ApplianceVirtualMachine = ""
  $ApplianceVirtualMachineArray = [System.Collections.ArrayList]@()
  Write-DebugLog "Checking if the VM exists"
  $tagAssignment = $null
  try {
    $tagAssignment = Get-TagAssignment -Tag $tagName -Category $tagCategoryName  -ErrorAction SilentlyContinue
  }
  catch {
    if ($tagAssignment) { 
      Write-DebugLog "Inside catch block but tagAssignment is not empty"
      if ($tagAssignment.Entity.Name) {
        Write-DebugLog "Appliance name "$tagAssignment.Entity.Name | Out-String
      }
    }
    Write-DebugLog $_.exception | Out-String
  }
  if ($tagAssignment) {
    Write-DebugLog "Tag Present"
    if ($tagAssignment.Entity.Name -is [array]) {
      Write-DebugLog "More than one VM present with tag AVS_ANF_CLOUD_ADMIN_VM_TAG"
      $noOfVM = $tagAssignment.Entity.Name.Count
      For ($i = 0; $i -lt $noOfVM; $i++) { 
        $ApplianceVirtualMachineArray.Add($tagAssignment.Entity.Name[$i]) | Out-Null
      }
    }
    else {
      Write-DebugLog "One VM present with tag AVS_ANF_CLOUD_ADMIN_VM_TAG"
      $ApplianceVirtualMachineArray.Add($tagAssignment.Entity.Name) | Out-Null
      Write-DebugLog  "First element inside ApplianceVirtualMachineArray: "$ApplianceVirtualMachineArray[0]
      $totalNumberofVM = $ApplianceVirtualMachineArray.Count
      Write-DebugLog  "No of VMs are: $totalNumberofVM"
    }  

    if ($ApplianceVirtualMachineArray.Count -lt 1) {
      Write-DebugLog "VM with the tag $tagName does not exist"
      return "";
    } 

    if ($ApplianceVirtualMachineArray.Count -eq 1) {
      return $ApplianceVirtualMachineArray[0]
    }
  
    if ($ApplianceVirtualMachineArray.Count -gt 1) {
      Write-DebugLog -Message "Below Virtual Machine appliance assosiated with the tag $tagName and tag categoy $tagCategoryName."
   
      foreach ($element in $ApplianceVirtualMachineArray) {
        Write-DebugLog -Message $element
      }
      $ApplianceVirtualMachine = Read-Host "Appliance Virtual Machine Name "
      return  $ApplianceVirtualMachine
    }
  }
}
function Test-ApplianceExistOrNot {

  param(
    [Parameter(Mandatory = $true)]
    $ApplianceVirtualMachine
  )

  Write-DebugLog -Message "Test-ApplianceExistOrNot execution started"

  $VMObject = $null
  try {
    $VMObject = Get-VM -Name $ApplianceVirtualMachine -ErrorAction Stop
  }
  catch {
    if ($_.CategoryInfo.Category -eq "ObjectNotFound") {
      Write-DebugLog -Message "VM with name $ApplianceVirtualMachine does not exist"
      Write-Error -ErrorId "UNINSTALL-NO-SUCH-VM-ERROR" -Message "VM with name $ApplianceVirtualMachine does not exist"
      write-error $_.exception.Message -ErrorAction Stop
      
    }
    else {
      Write-DebugLog -Message $_.exception | Out-String
      write-error $_.exception.Message -ErrorAction Stop
    }
  }

  if ($VMObject) {    
    Write-DebugLog -Message "Virtual Machine exist"    
  }
  else {
    Write-DebugLog -Message "VM with name $ApplianceVirtualMachine does not exist"
    Write-DebugLog -Message "VM with name $ApplianceVirtualMachine does not exist"
    write-error "VM with name $ApplianceVirtualMachine does not exist" -ErrorAction Stop
  }
  Write-DebugLog -Message "Test-ApplianceExistOrNot execution completed"
  return $VMObject
}

function Test-DataStore {

  param(
    [Parameter(Mandatory = $true)]
    [String]
    $VmDatastore,

    [Parameter(Mandatory = $true)]
    [Int]
    $FreeSapce
  )

  Write-DebugLog -Message "Test-DataStoreExistOrNot execution started"

  try {
    $datastore = Get-Datastore | Where-Object { $_.Name -eq $VmDatastore }

    if ($datastore) {
      Write-DebugLog "Datastore exists"
      
      $ds_space_gb = $datastore.FreeSpaceGB
      if ($ds_space_gb -lt $FreeSapce) {
        write-error "The datastore provided for deployment does not have the required free space. The minimum required space is: $FreeSapce GB" -ErrorAction Stop
      }
      else {
        Write-DebugLog -Message "SUCCESS: Datastore has enough free space for ova deployment."
      }
    }
    else {
      write-error "Invalid datastore" -ErrorAction Stop
    }
  }
  catch {
    write-error $_.exception.Message -ErrorAction Stop
  } 
}
function Test-EsxHost {

  <#
  .SYNOPSIS
      Tests one or more IP Addresses to determine if they are valid.
   
  .DESCRIPTION
      Test-EsxHost is a function that validates EsxHost for vCenter.
   
  .PARAMETER EsxHost
      EsxHost IP address. This parameter is mandatory.
 
  .EXAMPLE
       Test-EsxHost -EsxHost '192.168.0.1'
   
  .INPUTS
      String
   
  .OUTPUTS
      Boolean
  #>

  
  [CmdletBinding()]
  param (
    [Parameter(Mandatory = $true,
      ValueFromPipeLine = $true)]
    [string]$EsxHost
  )
  
  PROCESS {  
    
    $VMHostNetworkAdapterList = Get-VMHostNetworkAdapter | Select-Object VMhost, Name, IP, SubnetMask
  
    $isValidEsxHost = $false;
    foreach ($VMHostNetworkAdapter in $VMHostNetworkAdapterList) {  
      
      $VMHostIpAddres = $VMHostNetworkAdapter.IP
      if ($null -ne $VMHostIpAddres -and $VMHostIpAddres -ne "" -and $VMHostIpAddres -eq $EsxHost) {
        $isValidEsxHost = $true
        break
      }    
      $VMHost = $VMHostNetworkAdapter.VMHost
      if ($null -ne $VMHost -and $VMHost -ne "" -and $VMHost -eq $EsxHost) {
        $isValidEsxHost = $true
        break
      }
    }
    Write-Output $isValidEsxHost  
  }  
}

function Test-IpAddress {

  <#
  .SYNOPSIS
      Tests one or more IP Addresses to determine if they are valid.
   
  .DESCRIPTION
      Test-IpAddress is a function that tests one or more IP Addresses to determine if
      they are valid. The detailed parameter can be used to return additional information about the IP.
   
  .PARAMETER IpAddress
      One or more IP Addresses to test. This parameter is mandatory.
   
  .EXAMPLE
       Test-IpAddress -IpAddress '192.168.0.1', '192.168.0.256'
   
  .EXAMPLE
       Test-IpAddress -IpAddress '192.168.0.1'
   
  .EXAMPLE
       '::1', '192.168.0.256' | Test-MrIpAddress
   
  .INPUTS
      String
   
  .OUTPUTS
      Boolean
  #>

  
  [CmdletBinding()]
  param (
    [Parameter(Mandatory = $true,
      ValueFromPipeLine = $true)]
    [string[]]$IpAddress
  )
  
  PROCESS {  
    foreach ($Ip in $IpAddress) {  
      try {
        $Results = $Ip -match ([IPAddress]$Ip)
      }
      catch {
        Write-Output $false
        Continue
      }
      Write-Output $Results  
    }  
  }  
}

Function Get-VMToolsStatus {
 
  [CmdletBinding()]
  param (
    [Parameter(Mandatory = $true,
      ValueFromPipeLine = $true)]
    [string[]]$ApplianceVirtualMachine
  )
   
  PROCESS {
    try {
      $InputObject = Get-VM $ApplianceVirtualMachine
      [PSCustomObject]@{                    
        Name          = $InputObject.Name
        Status        = $InputObject.ExtensionData.Guest.ToolsStatus
        GuestState    = $InputObject.ExtensionData.Guest.GuestState
        UpgradeStatus = $InputObject.ExtensionData.Guest.ToolsVersionStatus2
        Version       = $InputObject.ExtensionData.Guest.ToolsVersion
      }
    }
    catch {
      Write-Error $_.Exception.Message
 
    }
  }
}

function Test-Configuration {

  param(
    [Parameter()]
    [string]
    $ApplianceIPAddress,

    [Parameter()]
    [string]
    $ApplianceVirtualMachine
  )

  BEGIN {
    Write-DebugLog -Message "In begin block of Test-Configuration."
  
    $scvExtension1 = "com.netapp.aegis"
    $scvExtension2 = "com.netapp.scvm.webclient"
    
    $maxNoOfTimesToLoop = 30
  }  

  PROCESS {

    Write-DebugLog -Message "Test-Configuration execution is started."

    $em = Get-View ExtensionManager
    $extensionList = $em.ExtensionList.Key
    $IsRegistered = $false
    $IsIPInExtensionData = $false
    $IsVMRestartedOnce = $false
    for ($Counter = 1; $Counter -lt $maxNoOfTimesToLoop; $Counter++) {
      ## unregistering from GUI
      $em = Get-View ExtensionManager
      $extensionList = $em.ExtensionList.Key
      if ($extensionList -contains $scvExtension1 -and $extensionList -contains $scvExtension2) {      
        # In case of static ip address
        if ($ApplianceIPAddress -eq "" -or $null -eq $ApplianceIPAddress) {
          # Get IPAddress assigned to VM
          $ApplianceIPAddress = (Get-VM | Select-Object Name, @{N = "IP Address"; E = { @($_.guest.IPAddress[0]) } } | Where-Object { $_.Name -Match $ApplianceVirtualMachine }).'IP Address'
        }
        
        $IsRegistered = (Get-View ExtensionManager | Select-Object -ExpandProperty ExtensionList | Select-Object Key, @{N = 'Server'; E = { $_.Server.url } } |  Where-Object { $_.Key -eq "com.netapp.aegis" }).Server -Match $ApplianceIPAddress
        Write-DebugLog -Message "isRegistered : $IsRegistered"
        $vmObject = Get-VM -Name  $ApplianceVirtualMachine
        $IsIPInExtensionData = $vmObject.ExtensionData.Guest.IpAddress -eq $ApplianceIPAddress
        
        Write-DebugLog "IsIPInExtensionData : $IsIPInExtensionData"
        
        if (($IsRegistered -eq $true) -and $IsIPInExtensionData -eq $true) {
          Write-DebugLog -Message "Verification is successful."         
          break
        }
      }    
      
     
      <#
      If this is end of the loop but installation is still not completed,
      then restart the VM and again start the verification cycle.
      #>

      if ($Counter -eq 15) {
        
        Write-DebugLog "IsVMRestartedOnce : $IsVMRestartedOnce" 
        if ($IsVMRestartedOnce -eq $false) {
          Write-DebugLog -Message "Inside IsVMRestartedOnce is false."            
          Write-DebugLog -Message "IsVMRestartedOnce : $IsVMRestartedOnce"                 
          Write-DebugLog -Message "Restart-VMwareVM -ApplianceVirtualMachine $ApplianceVirtualMachine"            
          Restart-VMwareVM -ApplianceVirtualMachine $ApplianceVirtualMachine                
          $IsVMRestartedOnce = $true  
          Start-Sleep 60                       
          Write-DebugLog -Message "Verifying Virtual Machine's IP address."  
        }
        else {
          Write-Error -Message "Registration failed" -ErrorAction Stop
        }   
      }
      else {
        Write-DebugLog "Start-Sleep 60"
        Start-Sleep 60
      }  
        
      $PercentComplete = ($Counter / $maxNoOfTimesToLoop) * 100
    }

    if ($IsRegistered -eq $false) {
      Write-DebugLog "Could not register cloud backup service."                
      write-error "Could not register cloud backup service." -ErrorAction Stop
    }

    if ($IsIPInExtensionData -eq $false) {
      Write-DebugLog "Could not assign IP address for virtual machine appliance."                
      write-error "Could not assign IP address for virtual machine appliance." -ErrorAction Stop
    }
  }
  END {
    Write-DebugLog -Message "Test-Configuration execution is completed."
  }
}

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-DebugLog -Message "SUCCESS: Check for powershell version passed"
  }
}

function checkModule {
  param(
    [String]$module
  )

  $module_exists = Get-Module -Name $module
  if ($module_exists) {
    Write-DebugLog -Message "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 {
  try {
    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-DebugLog -Message "SUCCESS: VCenter Address is set."
    }
  }
  catch {
    Write-Error "VC_ADDRESS is not set as a powershell variable. Please set it and run the script again" -ErrorAction Stop
  }
  
}

function Invoke-PreflightNetAppCBSAVSCheck {
  [CmdletBinding()]
  param()

  Process {
    checkPSVersion
    checkVcaddress
    checkModule "VMware.VimAutomation.Core"
    checkModule "VMware.vSphere.SsoAdmin"
    checkModule "Microsoft.AVS.Management"
  }
}

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

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

# Import Module Advanced Functions Implementation
Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object {
  Write-DebugLog -Message "Importing file: $($_.BaseName)"
  try {
    .$_.FullName
  }
  catch {
    Write-Error -Message "Failed to import functions from $($_.Fullname): $_"
  }
}