ZertoAVSModule.psm1
using module Microsoft.AVS.Management $PUBLIC_KEY = ('{0}/ZertoPublicKey.pem' -f $psScriptRoot) $ZERTO_FOLDER_ON_HOST = "/var/zerto" $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES = ('{0}/filesFromDatastore/' -f $psScriptRoot) Function TestConnection { return "TestConnection" } Function GenerateRandomPassword { $length = Get-Random -Minimum 8 -Maximum 24 # seedArray holds only valid characters for the password by removing double quotes, slash and black slash $seedArray = (48..57) + (65..90) + (97..122) + @(33, 36, 37, 94) $seedArray = $seedArray | Sort-Object Get-Random $asciiCharsList = @() foreach ($a in $seedArray){ $asciiCharsList += , [char][byte]$a } #regExp includes at least one uppercase, lowercase, number & special character (@!#$%?^) and ensure password is between 8 to $length characters long $regExp = "^((?=.*[a-z])(?=.*[A-Z])(?=.*[^A-Za-z0-9]))([A-Za-z\d@!#$%?^]){8,$length}$" do { $password = "" $loops = 1..$length Foreach ($loop in $loops) { $password += $asciiCharsList | Get-Random } } until ($password -match $regExp ) return $password } Function CreateZertoUser { <# .DESCRIPTION Create a ZertoDR user and a ZertoDR role which includes required privileges. The script creates a permission by assigning the ZertoDR role to the ZertoDR user. .PARAMETER zertoUsername Zerto user name (defaule value is ZertoDR) .PARAMETER zertoRole Zerto role name (defaule value is ZertoRole) .PARAMETER group An existing group to assign Zerto user to (Optional) .EXAMPLE CreateZertoUser -zertoUsername <Username> -zertoRole <Role> -group <Group> #> [CmdletBinding()] [AVSAttribute(30, UpdatesSDDC = $false)] param( [parameter(Mandatory=$false, HelpMessage = "Zerto user name for ZVM installation")] [string]$zertoUsername = "ZertoDR", [parameter(Mandatory=$false, HelpMessage = "Zerto role name for ZVM installation")] [string]$zertoRole = "ZertoRole", [parameter(Mandatory=$false, HelpMessage = "Group name for Zerto user")] [string]$group ) Process{ $domain = "vsphere.local" $zertoPrincipal = $domain + "\" + $zertoUsername $zertoPrivileges = @( "Alarm.Create", "Alarm.Delete", "Authorization.ModifyPermissions", "Cryptographer.Access", "Datastore.AllocateSpace", "Datastore.Browse", "Datastore.Config", "Datastore.DeleteFile", "Datastore.FileManagement", "Datastore.UpdateVirtualMachineFiles", "StoragePod.Config", "Extension.Register", "Extension.Unregister", "Folder.Create", "Global.CancelTask", "Global.Diagnostics", "Global.DisableMethods", "Global.EnableMethods", "Global.LogEvent", "Host.Config.AdvancedConfig", "Host.Config.AutoStart", "Host.Config.Settings", "Host.Config.NetService", "Host.Config.Patch", "Host.Inventory.EditCluster", "Network.Assign", "Resource.AssignVAppToPool", "Resource.AssignVMToPool", "Resource.ColdMigrate", "Resource.HotMigrate", "Sessions.ValidateSession", "Task.Create", "Task.Update", "VApp.ApplicationConfig", "VApp.AssignResourcePool", "VApp.AssignVM", "VApp.Create", "VApp.Delete", "VApp.Import", "VApp.PowerOff", "VApp.PowerOn", "VirtualMachine.Config.AddExistingDisk", "VirtualMachine.Config.AddNewDisk", "VirtualMachine.Config.AddRemoveDevice", "VirtualMachine.Config.AdvancedConfig", "VirtualMachine.Config.CPUCount", "VirtualMachine.Config.DiskExtend", "VirtualMachine.Config.EditDevice", "VirtualMachine.Config.ManagedBy", "VirtualMachine.Config.Memory", "VirtualMachine.Config.RawDevice", "VirtualMachine.Config.RemoveDisk", "VirtualMachine.Config.Resource", "VirtualMachine.Config.Settings", "VirtualMachine.Config.SwapPlacement", "VirtualMachine.Config.UpgradeVirtualHardware", "VirtualMachine.Interact.PowerOff", "VirtualMachine.Interact.PowerOn", "VirtualMachine.Inventory.CreateFromExisting", "VirtualMachine.Inventory.Create", "VirtualMachine.Inventory.Register", "VirtualMachine.Inventory.Delete", "VirtualMachine.Inventory.Unregister", "VirtualMachine.State.RemoveSnapshot" ) $PersistentSecrets.ZertoPassword = GenerateRandomPassword New-SsoPersonUser -UserName $zertoUsername -Password $PersistentSecrets.ZertoPassword -Description "Zerto DR user" -EmailAddress "ZertoDR@zerto.com" -FirstName "Zerto" -LastName "DR" | Out-Null # Add user to group if ($group) { $SsoGroup = Get-SsoGroup -Name $group -Domain $domain Get-SsoPersonUser -Name $zertoUsername -Domain $domain | Add-UserToSsoGroup -TargetGroup $SsoGroup } # Create a new role New-VIRole -name $zertoRole -Privilege (Get-VIPrivilege -Server $VC_ADDRESS -id $zertoPrivileges) -Server $VC_ADDRESS | Out-Null # Get the Root Folder $rootFolder = Get-Folder -NoRecursion # Create permission on vCenter object by assigning role to user New-VIPermission -Entity $rootFolder -Principal $zertoPrincipal -Role $zertoRole -Propagate:$true | Out-Null } } Function CreateZertoFolderOnHost { param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName ) process { $Command = "mkdir -p $ZERTO_FOLDER_ON_HOST" $Res = RunSSHCommands -HostName $HostName -Commands $Command $ExitStatus = $Res["0_exitStatus"]; if ( $ExitStatus -ne '0' ) { throw "failed to create $ZERTO_FOLDER_ON_HOST on host $HostName. Exit status for ""$Command"" is $ExitStatus" } } } Function VerifyAndUploadFilesFromPSEngineToHost { param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName ) CreateZertoFolderOnHost -HostName $HostName foreach ($file in Get-ChildItem $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES* -Include *.sh, *.o) { $signature = ("{0}_signature" -f $file) $isVerified = (openssl dgst -sha256 -verify $PUBLIC_KEY -signature $signature $file 2>&1) -join ";" if ($isVerified -eq "Verified OK") { Set-SFTPItem -SessionId ($SFTP_Sessions[$HostName]).Value.SessionId -Destination $ZERTO_FOLDER_ON_HOST -Path $file -Force } else { throw "Error! host $HostName failed to verify $file with $signature, openSSL output: $isVerified" } } } Function DownloadFilesFromDatastoreToPSEngine { param( [Parameter(Mandatory = $true, HelpMessage = "Datastore Uuid")] [string]$DatastoreUuid, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid ) $psDriverName = "ds" $FullRemoteFileLocation = ('{0}:\zagentid\{1}\*' -f $psDriverName, $BiosUuid) $datastore = Get-Datastore $DatastoreUuid New-PSDrive -Location $datastore -Name $psDriverName -PSProvider VimDatastore -Root "\" Copy-DatastoreItem -Item $FullRemoteFileLocation -Destination $LOCAL_TEMP_FOLDER_FOR_DOWNLOADED_FILES -Force Remove-PSDrive -Name $psDriverName } Function CopyFilesFromDatastoreToHost { param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName, [Parameter(Mandatory = $true, HelpMessage = "Datastore Uuid")] [string]$DatastoreUuid, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid ) DownloadFilesFromDatastoreToPSEngine -DatastoreUuid $DatastoreUuid -BiosUuid $BiosUuid VerifyAndUploadFilesFromPSEngineToHost -HostName $HostName } Function RunSSHCommands { param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName, [Parameter(Mandatory = $true, HelpMessage = "Commands to execute")] [String[]]$Commands ) process { $NamedOutputs = @{} Set-Variable -Name NamedOutputs -Value $NamedOutputs -Scope Global $i = 0 foreach ($Command in $Commands) { $SSH = Invoke-SSHCommand -SSHSession $SSH_Sessions[$HostName].Value -Command $Command if (!$SSH) { throw "Error! failed to Invoke-SSHCommand ""$Command"" on host $HostName" } $ExitStatus = $SSH.ExitStatus $Error = $SSH.Error if ($ExitStatus -ne 0 -Or $Error) { throw "Error! failed to run ""$Command"" on host $HostName, exitStatus: $ExitStatus, error: $Error" } $NamedOutputs["$($i)_cmd"] = $Command $NamedOutputs["$($i)_exitStatus"] = $ExitStatus $NamedOutputs["$($i)_output"] = ($SSH.Output -join ";") $NamedOutputs["$($i)_error"] = $Error $i++; } return $NamedOutputs } } Function Get-HostTempFolderInfo { <# .DESCRIPTION Display information about the available disk space (For Internal Use) .PARAMETER HostName Host Name to connect with ssh .EXAMPLE Get-HostTempFolderInfo -HostName xxx.xxx.xxx.xxx #> [CmdletBinding()] [AVSAttribute(5, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName ) process { $Command = "vdf" return RunSSHCommands -HostName $HostName -Commands $Command } } Function EnsureConnectivity { <# .DESCRIPTION Check if the host is up and running (For Internal Use) .PARAMETER HostName Host Name to connect with ssh .EXAMPLE EnsureConnectivity -HostName xxx.xxx.xxx.xxx #> [CmdletBinding()] [AVSAttribute(5, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName ) process { $Command = "echo testing123" return RunSSHCommands -HostName $HostName -Commands $Command } } Function Get-HostEsxiVersion { <# .DESCRIPTION Retrieve the ESXi version (For Internal Use) .PARAMETER HostName Host Name to connect with ssh .EXAMPLE Get-HostEsxiVersion -HostName xxx.xxx.xxx.xxx #> [CmdletBinding()] [AVSAttribute(5, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName ) process { $Command = "vmware -l" return RunSSHCommands -HostName $HostName -Commands $Command } } Function ChangeStartupFile { <# .DESCRIPTION Responsible for loading the driver when the host is booting. /etc/rc.local.d/local.sh file is executed after all the normal system services are started .PARAMETER HostName Host Name to connect with ssh .PARAMETER DatastoreUuid Datastore Uuid .PARAMETER BiosUuid "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid" .EXAMPLE ChangeStartupFile -HostName xxx.xxx.xxx.xxx -DatastoreUuid xxx -BiosUuid xxx #> [CmdletBinding()] [AVSAttribute(30, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName, [Parameter(Mandatory = $true, HelpMessage = "Datastore Uuid")] [string]$DatastoreUuid, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid ) Process { $zloadmod = ('{0}/zloadmod.sh' -f $ZERTO_FOLDER_ON_HOST) CopyFilesFromDatastoreToHost -HostName $HostName -DatastoreUuid $DatastoreUuid -BiosUuid $BiosUuid $startupFile = ('{0}/startup_file.sh' -f $ZERTO_FOLDER_ON_HOST) $Commands = ('grep -v "ZeRTO\|exit 0" /etc/rc.local.d/local.sh > {0}' -f $startupFile), ('echo \#ZeRTO\ >> {0}' -f $startupFile), ('echo sh {0} load {1} {2} \"\" \"\" 1 \> /etc/vmware/zloadmod.txt \2\>\&\1 \#ZeRTO\ >> {3}' -f $zloadmod, $DatastoreUuid, $BiosUuid, $startupFile), ('echo \#ZeRTO\ >> {0}' -f $startupFile), ('echo "exit 0" >> {0}' -f $startupFile), ('cp -f {0} /etc/rc.local.d/local.sh' -f $startupFile), ('chmod a+x {0}' -f $zloadmod) return RunSSHCommands -HostName $HostName -Commands $Commands } } Function InstallDriver { <# .DESCRIPTION Install the driver .PARAMETER HostName Host Name to connect with SSH .PARAMETER DatastoreUuid Datastore Uuid .PARAMETER BiosUuid Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid .PARAMETER IsInit Init or load the driver .PARAMETER EsxiVersion Esxi version .EXAMPLE InstallDriver -HostName xxx.xxx.xxx.xxx -DatastoreUuid <UUID> -BiosUuid <UUID> -IsInit <init/load> -EsxiVersion xx #> [CmdletBinding()] [AVSAttribute(30, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName, [Parameter(Mandatory = $true, HelpMessage = "Datastore Uuid")] [string]$DatastoreUuid, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid, [Parameter(Mandatory = $true, HelpMessage = "Init or load the driver")] [string]$IsInit, [Parameter(Mandatory = $true, HelpMessage = "Esxi version")] [string]$EsxiVersion ) Process { $zloadmod = ('{0}/zloadmod.sh' -f $ZERTO_FOLDER_ON_HOST) CopyFilesFromDatastoreToHost -HostName $HostName -DatastoreUuid $DatastoreUuid -BiosUuid $BiosUuid $Commands = ('chmod a+x {0}' -f $zloadmod), ('{0} {1} {2} {3} 1 {4} 1 > /etc/vmware/zloadmod.txt' -f $zloadmod, $IsInit, $DatastoreUuid, $BiosUuid, $EsxiVersion) return RunSSHCommands -HostName $HostName -Commands $Commands } } Function UninstallDriver { <# .DESCRIPTION Uninstall the driver .PARAMETER HostName Host Name to connect with SSH .PARAMETER DatastoreUuid Datastore Uuid .PARAMETER BiosUuid Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid .EXAMPLE UninstallDriver -HostName xxx.xxx.xxx.xxx -DatastoreUuid <UUID> -BiosUuid <UUID> #> [CmdletBinding()] [AVSAttribute(30, UpdatesSDDC = $false)] param( [Parameter(Mandatory = $true, HelpMessage = "Host Name to connect with SSH")] [string]$HostName, [Parameter(Mandatory = $true, HelpMessage = "Datastore Uuid")] [string]$DatastoreUuid, [Parameter(Mandatory = $true, HelpMessage = "Host Bios Uuid || mob-> Property Path: host.hardware.systemInfo.uuid")] [string]$BiosUuid ) process { $zunloadmod = ('{0}/zunloadmod.sh' -f $ZERTO_FOLDER_ON_HOST) CopyFilesFromDatastoreToHost -HostName $HostName -DatastoreUuid $DatastoreUuid -BiosUuid $BiosUuid $Commands = ('chmod a+x {0}' -f $zunloadmod), ('{0} cleanup > /etc/vmware/zunloadmod.txt' -f $zunloadmod) return RunSSHCommands -HostName $HostName -Commands $Commands } } |