validations_utils.ps1
$PUBLIC_KEY = ('{0}/ZertoPublicKey.pem' -f $psScriptRoot) $PASSWORD_REGEX = [regex]"^(?=.*[A-Z])(?=.*[^A-Za-z])(?=.*\d)(?=.*[\W_]).{8,}$" $UNSUPPORTED_FIRST_LETTER_IN_PASSWORD = "[]{}>|*&!%#`@," $UNSUPPORTED_LETTERS_IN_PASSWORD_REGEX_PATTERN = '[\$\s"]' function Validate-FileBySignature { param ( [Parameter(Mandatory = $true, HelpMessage = "File to verify")] [string]$FilePath, [Parameter(Mandatory = $true, HelpMessage = "Signature file to verify")] [string]$SignatureFilePath ) process { Write-Host "Verifying signature for $FilePath" $isVerified = (openssl dgst -sha256 -verify $PUBLIC_KEY -signature $SignatureFilePath $FilePath 2>&1) -join ";" if ($isVerified -eq "Verified OK") { Write-Host "File signature was verified successfully for $FilePath by $SignatureFilePath" return $true } else { Write-Host "Could not verify $FilePath signature by $SignatureFilePath" return $false } } } function Validate-NetworkSettings { param ( [Parameter(Mandatory = $true, HelpMessage = "ZVML ip address")] [string] $ZvmlIp, [Parameter(Mandatory = $true, HelpMessage = "SubnetMask address.")] [string] $SubnetMask, [Parameter(Mandatory = $true, HelpMessage = "Default gateway.")] [string] $DefaultGateway, [Parameter(Mandatory = $true, HelpMessage = "DNS server address.")] [string] $DNS ) process { $IpSettingsValidated = $true Write-Host "Validating ZVML IP $ZvmlIp" if ((Validate-IpAddress $ZvmlIp) -eq $false) { $IpSettingsValidated = $false $Message = "ZVM could not be configured with the specified IP address $ZvmlIp, due to invalid IP format." Write-Host $Message Write-Error $Message } Write-Host "Validating SubnetMask $SubnetMask" if ((Validate-IpAddress $SubnetMask) -eq $false) { $IpSettingsValidated = $false $Message = "ZVM could not be configured with the specified subnet mask $SubnetMask, due to invalid IP format." Write-Host $Message Write-Error $Message } Write-Host "Validating DNS IP $DNS" if ((Validate-IpAddress $DNS) -eq $false) { $IpSettingsValidated = $false $Message = "ZVM could not be configured with the specified DNS $DNS, due to invalid IP format." Write-Host $Message Write-Error $Message } Write-Host "Validating DefaultGateway IP $DefaultGateway" if ((Validate-IpAddress $DefaultGateway) -eq $false) { $IpSettingsValidated = $false $Message = "ZVM could not be configured with the specified default gateway $DefaultGateway, due to invalid IP format." Write-Host $Message Write-Error $Message } if ($IpSettingsValidated -eq $false) { Write-Error "provided IP addresses format is invalid" -ErrorAction Stop } } } function Validate-IpAddress { param ( [Parameter(Mandatory = $true, HelpMessage = "ip address")] [string]$ip ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." try { if (([ipaddress]$ip).IPAddressToString -eq ("" + $ip)) { Write-Host "IP address $ip is valid" return $true } else { Write-Host "ZVM could not be configured with the specified address $ip, due to wrong format. $_" return $false } } catch { Write-Host "ZVM could not be configured with the specified address $ip, due to wrong format. $_" return $false } } } function Validate-VcEnvParams { param( [Parameter(Mandatory = $true, HelpMessage = "Datastore Name")] [string]$DatastoreName, [Parameter(Mandatory = $true, HelpMessage = "ZVM IP")] [string]$ZVMLIp, [Parameter(Mandatory = $true, HelpMessage = "Subnet Mask")] [string]$SubnetMask, [Parameter(Mandatory = $true, HelpMessage = "Default Gateway")] [string]$DefaultGateway, [Parameter(Mandatory = $true, HelpMessage = "DNS Address")] [string]$DNS, [Parameter(Mandatory = $true, HelpMessage = "Network Name")] [string]$NetworkName, [Parameter(Mandatory = $true, HelpMessage = "Host Name")] [string]$HostName ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." if ((Test-VmExists -VmName $ZVM_VM_NAME) -eq $true) { Write-Error "$ZVM_VM_NAME already exists. Please remove it with 'Uninstall-Zerto' first." -ErrorAction Stop } if ((Validate-DatastoreName -DatastoreName $DatastoreName) -ne $true) { Write-Error "Datastore=$DatastoreName does not exist. Validation failed" -ErrorAction Stop } if ((Validate-NetworkName -NetworkName $NetworkName) -ne $true) { Write-Error "Network=$NetworkName does not exist. Validation failed" -ErrorAction Stop } Assert-HostConnectedToDatastoreAndNetwork -HostName $HostName -NetworkName $NetworkName -DatastoreName $DatastoreName Validate-NetworkSettings -ZvmlIp $ZVMLIp -SubnetMask $SubnetMask -DefaultGateway $DefaultGateway -DNS $DNS Write-Host "Validations finished" } } function Get-ValidatedHostName { param( [Parameter(Mandatory = $false, HelpMessage = "Host Name")] [string]$HostName ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." $AllValidHostNames = (Get-VMHost -ErrorAction SilentlyContinue | Where-Object { $_.ConnectionState -eq "Connected" -and $_.PowerState -eq "PoweredOn" }).Name if ($HostName) { Write-Host "Host provided by user: $HostName" $ValidHostName = $AllValidHostNames | Where-Object { $_ -eq $HostName } | Select-Object -First 1 } if ($ValidHostName) { Write-Host "Host=$ValidHostName exists. Validation successful" return $ValidHostName } else { if ($HostName) { Write-Error "Host=$HostName does not exist. Validation failed" -ErrorAction Stop } else { Write-Error "could not find a valid host." -ErrorAction Stop } } } } function Assert-HostConnectedToDatastoreAndNetwork ($HostName, $NetworkName, $DatastoreName) { Write-Host "Starting $($MyInvocation.MyCommand)..." $esxiHost = Get-VMHost -Name $HostName $datastore = Get-Datastore -Name $DatastoreName -VMHost $esxiHost if ($datastore) { Write-Host "Datastore '$DatastoreName' is accessible by the host '$HostName'." } else { throw "Datastore '$DatastoreName' is NOT accessible by the host '$HostName'." } $network = Get-VirtualPortGroup -Name $NetworkName -VMHost $esxiHost if ($network) { Write-Host "Network '$NetworkName' is accessible by the host '$HostName'." } else { throw "Network '$NetworkName' is NOT accessible by the host '$HostName'." } } function Validate-ZertoPassword { param ( [Parameter(Mandatory = $true)] [SecureString]$Password ) process { $isValid = $PASSWORD_REGEX.Matches($( ConvertFrom-SecureString -SecureString $Password -AsPlainText)).Success if ($isValid -ne $true) { Write-Error "Zerto password requirements are not met. Password should contain at least one uppercase letter, one digit, one non-alphanumeric character, and be at least 8 characters long." -ErrorAction Stop } if ($UNSUPPORTED_FIRST_LETTER_IN_PASSWORD.Contains($(ConvertFrom-SecureString -SecureString $Password -AsPlainText)[0])) { Write-Error "Zerto password requirements are not met. Password should not begin with the following characters $UNSUPPORTED_FIRST_LETTER_IN_PASSWORD ." -ErrorAction Stop } if ($(ConvertFrom-SecureString -SecureString $Password -AsPlainText) -match $UNSUPPORTED_LETTERS_IN_PASSWORD_REGEX_PATTERN) { Write-Error 'Zerto password requirements are not met. Password should not contain $, ", or a space characters.' -ErrorAction Stop } } } function Test-VmExists { param( [Parameter(Mandatory = $true, HelpMessage = "VM Name")] [string]$VmName ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." $VM = Get-VM -Name $VmName -ErrorAction SilentlyContinue | Select-Object -first 1 if ($null -eq $VM) { Write-Host "VM=$VmName does not exist" return $false } Write-Host "VM=$VmName exists" return $true } } function Validate-BiosUUID { param( [Parameter(Mandatory = $true, HelpMessage = "Datastore Name")] [string]$DatastoreName, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." $Datastore = Get-Datastore -Name $DatastoreName | Select-Object -first 1 New-PSDrive -Name TgtDS -Location $Datastore -PSProvider VimDatastore -Root '\' | Out-Null $DatastoreFolders = Get-ChildItem -Path TgtDS: -Recurse Remove-PSDrive -Name TgtDS foreach ($DatastoreFolder in $DatastoreFolders) { if ($DatastoreFolder.Name -eq $BiosUuid) { Write-Host "BiosUuid=$BiosUuid exists. Validation successful" return $true } } Write-Error "BiosUuid=$BiosUuid does not exist. Validation failed" return $false } } function Validate-DigitsOnly { param( [Parameter(Mandatory = $true, HelpMessage = "Input string to validate all the characters are numeric")] [string]$InputString ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." if ($InputString -match "^\d+$") { Write-Host "InputString=$InputString contains digits only" return $true } Write-Error "Validation failed. InputString=$InputString contains non-numeric characters" return $false } } function Validate-DatastoreName { param( [Parameter(Mandatory = $true, HelpMessage = "Datastore Name")] [string]$DatastoreName ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." $Datastore = Get-Datastore -Name $DatastoreName -ErrorAction SilentlyContinue | Select-Object -first 1 if ($null -eq $Datastore) { Write-Host "Datastore=$DatastoreName does not exist. Validation failed" return $false } Write-Host "Datastore=$DatastoreName exists. Validation successful" return $true } } function Validate-NetworkName { param( [Parameter(Mandatory = $true, HelpMessage = "Network Name")] [string]$NetworkName ) process { Write-Host "Starting $($MyInvocation.MyCommand)..." $Network = Get-VirtualNetwork -Name $NetworkName -ErrorAction SilentlyContinue | Select-Object -first 1 if ($null -eq $Network) { Write-Host "Network=$NetworkName does not exist. Validation failed" return $false } Write-Host "Network=$NetworkName exists. Validation successful" return $true } } function Validate-AvsParams { param ( [Parameter(Mandatory = $true, HelpMessage = "Tenant ID")][string] $TenantId, [Parameter(Mandatory = $true, HelpMessage = "Cleint ID")][string] $ClientId, [Parameter(Mandatory = $true, HelpMessage = "Client Secret")][SecureString] $ClientSecret, [Parameter(Mandatory = $true, HelpMessage = "Subscription ID")][string] $SubscriptionId, [Parameter(Mandatory = $true, HelpMessage = "Resource Group Name")][string] $ResourceGroupName, [Parameter(Mandatory = $true, HelpMessage = "Avs Cloud Name")][string] $AvsCloudName ) process { Write-Host "Starting $($MyInvocation.MyCommand)" $body = @{ 'client_id' = $ClientId 'client_secret' = (ConvertFrom-SecureString -SecureString $ClientSecret -AsPlainText) 'grant_type' = "client_credentials" 'resource' = "https://management.core.windows.net/" } try { $authResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$TenantId/oauth2/token" ` -Body $body -ContentType "application/x-www-form-urlencoded" } catch { Write-Error "Authorization failed for Azure. Please check the values of the TenantID-ClientID-ClientSecret combintaion." ` -ErrorAction Stop } $authToken = $authResponse.access_token $subscriptionUri = "https://management.azure.com/subscriptions/$SubscriptionId/?api-version=2020-01-01" try { [void] (Invoke-RestMethod -Method Get -Headers @{Authorization = ("Bearer " + $authToken) } -Uri $subscriptionUri) } catch { Write-Error "The subscription '$SubscriptionId' could not be found for the tenant '$TenantId'." -ErrorAction Stop } $avsCloudNameUri = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.AVS/privateClouds/$AvsCloudName/?api-version=2020-03-20" try { [void] (Invoke-RestMethod -Method Get -Headers @{Authorization = ("Bearer " + $authToken) } -Uri $avsCloudNameUri) } catch { Write-Error "The private cloud '$AvsCloudName' could not be found for the tenant '$TenantId'." -ErrorAction Stop } Write-Host "AVS parameters are valid" } } function Assert-ReconfigurationToken ($Token) { Write-Host "Starting $($MyInvocation.MyCommand)" try { $Url = "https://www.zerto.com/myzerto/wp-json/services/zerto/s3-ova-employee?key=" + $Token $response = Invoke-WebRequest -Uri $Url -ErrorAction Stop -TimeoutSec 1800 $content = $response.Content if ($content -ne '{"success":true}') { throw "Reconfiguration token is invalid." } Write-Host "Reconfiguration token is valid" } catch { throw "Reconfiguration token is invalid." } } |