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 the OVA package. The OVA package contains a virtual appliance.
 
.DESCRIPTION
Installs the OVA package. The OVA package contains a virtual appliance.
 
.PARAMETER ApplianceVirtualMachineName
Specify the virtual machine name for the appliance.
 
.PARAMETER EsxHost
Specify the destination ESXi hostname for the virtual appliance.
 
.PARAMETER VmDatastore
Specify the datastore to be used for the virtual appliance.
 
.PARAMETER NetworkMapping
 Specify the destination network for each source network
 
.PARAMETER ApplianceNetworkName
Specifies the appliance network name.
 
.PARAMETER ApplianceIPAddress
IPV4 address of the virtual appliances.
 
.PARAMETER Netmask
Specify the subnet to be used on the deployed network.
 
.PARAMETER Gateway
Gateway of the deployed network.
 
.PARAMETER PrimaryDNS
Primary DNS server IP address.
 
.EXAMPLE
Install-NetAppCBSAppliance -AcceptNetAppEulaAggrement $true -ApplianceVirtualMachineName "VM1" -EsxHost "10.212.11.119" -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 the OVA file using static IP address.
#>


function Install-NetAppCBSAppliance {
  [AVSAttribute(45, UpdatesSDDC = $True)]
  [CmdletBinding()]
  param(   
    [Parameter(
      Mandatory = $true,
      Position = 0,
      HelpMessage = "Do you accept End User License Aggrement 'https://www.netapp.com/pdf.html?item=/media/14114-enduserlicenseagreementworldwide.pdf' ?")]
    [ValidateNotNullOrEmpty()]
    [boolean]
    $AcceptNetAppEulaAggrement,

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

    [Parameter(
      Mandatory = $true,
      Position = 2,
      HelpMessage = 'Specify the destination ESXi hostname for the virtual appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $EsxHost,

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

    [Parameter(
      Mandatory = $true,
      Position = 4,
      HelpMessage = 'Specify the destination network for each source network')]
    [ValidateNotNullOrEmpty()]
    [string]
    $NetworkMapping,

    [Parameter(
      Mandatory = $true,
      Position = 5,
      HelpMessage = 'Specify the network name of the virtual appliance.')]
    [string]
    $ApplianceNetworkName,

    [Parameter(
      Mandatory = $true,
      Position = 6,
      HelpMessage = 'IPV4 address for the appliance')]
    [string]
    $ApplianceIPAddress,

    [Parameter(
      Mandatory = $true,
      Position = 7,
      HelpMessage = 'Subnet mask')]
    [string]
    $Netmask,

    [Parameter(
      Mandatory = $true,
      Position = 8,
      HelpMessage = 'Gateway on the deployed network')]
    [string]
    $Gateway,

    [Parameter(
      Mandatory = $true,
      Position = 9,
      HelpMessage = 'Primary DNS server IP address')]
    [string]
    $PrimaryDNS,

    [Parameter(
      Mandatory = $true,
      Position = 10,
      HelpMessage = 'Appliance user name.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,

    [Parameter(
      Mandatory = $true,
      Position = 11,
      HelpMessage = 'Appliance password')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword,

    [Parameter(
      Mandatory = $true,
      Position = 12,
      HelpMessage = 'Maint User password')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $MaintUserPassword
  )
  
  $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", $MaintUserPassword)
  Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -EsxHost $EsxHost -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -ApplianceIPAddress  $ApplianceIPAddress -Netmask $Netmask -Gateway $Gateway -PrimaryDNS $PrimaryDNS  -scvcredential $scvcredential -maintusercredential $maintusercredential
}

<#
.SYNOPSIS
Installs the OVA package. The OVA package contains a virtual appliance.
 
.DESCRIPTION
Installs the OVA package. The OVA package contains a virtual appliance.
 
.PARAMETER ApplianceVirtualMachineName
Specify the virtual machine name for the appliance.
 
.PARAMETER EsxHost
Specify the destination ESXi hostname for the virtual appliance.
 
.PARAMETER VmDatastore
Specify the datastore to be used for the virtual appliance.
 
.PARAMETER NetworkMapping
Specify the destination network for each source network
 
.PARAMETER ApplianceNetworkName
Specifies the appliance network name.
 
.EXAMPLE
Install-NetAppCBSApplianceUsingDHCP -AcceptNetAppEulaAggrement $true -ApplianceVirtualMachineName "VM1" -EsxHost "10.212.11.119" -VmDatastore "datastore1" -NetworkMapping "VM Network" -ApplianceNetworkName "scvova"
 
Description
---------------------------------------
Installs the OVA file using dynamic IP address.
#>


function Install-NetAppCBSApplianceUsingDHCP {
  [AVSAttribute(45, UpdatesSDDC = $True)]
  [CmdletBinding()]
  param(  
    [Parameter(
      Mandatory = $true,
      Position = 0,
      HelpMessage = "Do you accept End User License Aggrement 'https://www.netapp.com/pdf.html?item=/media/14114-enduserlicenseagreementworldwide.pdf' ?")]
    [ValidateNotNullOrEmpty()]
    [boolean]
    $AcceptNetAppEulaAggrement,

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

    [Parameter(
      Mandatory = $true,
      Position = 2,
      HelpMessage = 'Specify the destination ESXi hostname for the virtual appliance.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $EsxHost,

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

    [Parameter(
      Mandatory = $true,
      Position = 4,
      HelpMessage = 'Specify the destination network for each source network')]
    [ValidateNotNullOrEmpty()]
    [string]
    $NetworkMapping,

    [Parameter(
      Mandatory = $true,
      Position = 5,
      HelpMessage = 'Specify the network name of the virtual appliance.')]
    [string]  
    $ApplianceNetworkName,

    [Parameter(
      Mandatory = $true,
      Position = 6,
      HelpMessage = 'Appliance user name.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,

    [Parameter(
      Mandatory = $true,
      Position = 7,
      HelpMessage = 'Appliance password')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword
  )

  $scvcredential = New-Object System.Management.Automation.PSCredential ($ApplianceUser, $AppliancePassword)
  Install-Appliance -AcceptNetAppEulaAggrement $AcceptNetAppEulaAggrement -ApplianceVirtualMachine $ApplianceVirtualMachineName -EsxHost $EsxHost -VmDatastore $VmDatastore -NetworkMapping $NetworkMapping -ApplianceNetworkName $ApplianceNetworkName -Dhcp $true -scvcredential $scvcredential
}

<#
.SYNOPSIS
Upgrades OVA package in the virtual appliance.
 
.DESCRIPTION
Upgrades OVA package in the virtual appliance.
 
.PARAMETER VmDatastore
Specify the datastore for the virtual appliance.
 
.EXAMPLE
Update-NetAppCBSAppliance -VmDatastore "ova_ds"
Description
---------------------------------------
Upgrades the virtual appliance
#>


function Update-NetAppCBSAppliance { 
  
  [CmdletBinding()]
  param(    
    
    [Parameter(
      Mandatory = $true,
      Position = 0,
      HelpMessage = 'Storage for the configuration and disk files')]
    [string]
    $VmDatastore,
    
    [Parameter(
      Mandatory = $true,
      Position = 1,
      HelpMessage = 'Appliance user name.')]
    [ValidateNotNullOrEmpty()]
    [string]
    $ApplianceUser,

    [Parameter(
      Mandatory = $true,
      Position = 3,
      HelpMessage = 'Appliance password')]
    [ValidateNotNullOrEmpty()]
    [Security.SecureString]
    $AppliancePassword
  )

  $scvcredential = New-Object System.Management.Automation.PSCredential ($ApplianceUser, $AppliancePassword)

  update-netapp-cbs-appliance -VmDatastore "ova_ds" -scvcredential  $scvcredential
}

<#
.SYNOPSIS
Modifies the password of the virtual appliance.
 
.DESCRIPTION
Modifies the password of the virtual appliance.
 
.EXAMPLE
Set-NetAppCBSAppliancePassword
 
Description
---------------------------------------
Modify the password of the virtual appliance.
 
#>

function Set-NetAppCBSAppliancePassword {
  <#
   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.
  #>


  set-netapp-cbs-appliance-password
}

<#
.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 = 'Specify $True to UnInstall NetApp CBS Appliance. In interactive mode enter any value to uninstall and leave it blank to cancel the operation.')]
    [bool]
    $AreYouSureToDelete
  )
  
  if ($AreYouSureToDelete -eq $True) {
    uninstall-netapp-cbs-appliance
  }  
}
# 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 "-Activity Shutdown-VMGuest -Status $i/$count : $PercentComplete % "
        }
        
        Write-DebugLog -Message "-Activity Shutdown-VMGuest -Status Done -complete"
        
        Start-VM -VM $ApplianceVirtualMachine | 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 "-Activity Start-VM -Status $i/$count : $PercentComplete"
        }
        Write-DebugLog -Message "-Activity Start-VM -Status Done -complete"
        
      }
      catch {
        Write-DebugLog -Message  $_.exception
        Write-Error -ErrorId "MOUNT_FAILED" -Message $_.exception.Message
      } 
          
    }
    else {
          
      Start-VM -VM $ApplianceVirtualMachine | 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 "-Activity Start-VM -Status $i/$count : $PercentComplete % Complete -PercentComplete $PercentComplete"
      }
      Write-DebugLog -Message "-Activity Start-VM -Status Done -complete"
        
    }
  }     

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


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-OVA -StorageAccountName 'acountname' -ContainerName 'container'
 
Description
---------------------------------------
Downloads file from Azure account.
 
#>


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

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

    [Parameter(Mandatory)]
    [string]
    $ContainerName
  )

  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"
    (New-Object System.Net.WebClient).DownloadFile($FileUrl, "$GetLocation/$BlobName")
    Write-DebugLog -Message "The file $BlobName downloaded"
    Write-DebugLog -Message "List of files in $GetLocation"
    (Get-ChildItem -Path $GetLocation).Name
    Write-DebugLog -Message "Downloaded file saved in the following file system folder"  $GetLocation
    

  }
  catch {
    Write-DebugLog -Message $_.exception.Message
    write-error $_.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) {
  Write-DebugLog -Message "Get-RandomCharacters execution is started"
  $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
  $private:ofs = ""
  Write-DebugLog -Message "Get-RandomCharacters execution is completed"
  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]
      Write-DebugLog  "No of VMs are: "$ApplianceVirtualMachineArray.Count
    }  

    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
  }

  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 }

    $ds_space_gb = $datastore.FreeSpaceGB
    if ($ds_space_gb -lt $FreeSapce) {
      write-error "Datastore provided for deployment doesn't have enough free space for the deployment. Minimum required $FreeSapce GB" -ErrorAction Stop
    }
    else {
      Write-DebugLog -Message "SUCCESS: Datastore has enough free space for ova deployment."
    }
  }
  catch {
    write-error $_.exception.Message -ErrorAction Stop
  } 

  if ($datastore) {
    Write-DebugLog "Datastore exists"
  }
  else {
    write-error "Invalid datastore" -ErrorAction Stop
  }
}

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 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): $_"
  }
}