Migrate.Autorest/custom/New-AzMigrateLocalServerReplication.ps1
|
# ---------------------------------------------------------------------------------- # # Copyright Microsoft Corporation # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ---------------------------------------------------------------------------------- <# .Synopsis Starts replication for the specified server. .Description The New-AzMigrateLocalServerReplication cmdlet starts the replication for a particular discovered server in the Azure Migrate project. .Link https://learn.microsoft.com/powershell/module/az.migrate/new-azmigratelocalserverreplication #> function New-AzMigrateLocalServerReplication { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Runtime.PreviewMessageAttribute("This cmdlet is based on a preview API version and may experience breaking changes in future releases.")] [OutputType([Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.IJobModel])] [CmdletBinding(DefaultParameterSetName = 'ByIdDefaultUser', PositionalBinding = $false, SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter(ParameterSetName = 'ByIdDefaultUser', Mandatory)] [Parameter(ParameterSetName = 'ByIdPowerUser', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the machine ARM ID of the discovered server to be migrated. ${MachineId}, [Parameter(Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the storage path ARM ID where the VMs will be stored. ${TargetStoragePathId}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.Int32] # Specifies the number of CPU cores. ${TargetVMCPUCore}, [Parameter(ParameterSetName = 'ByIdDefaultUser', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the logical network ARM ID that the VMs will use. ${TargetVirtualSwitchId}, [Parameter(ParameterSetName = 'ByIdDefaultUser')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the test logical network ARM ID that the VMs will use. ${TargetTestVirtualSwitchId}, [Parameter()] [ValidateSet("true" , "false")] [ArgumentCompleter( { "true" , "false" })] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies if RAM is dynamic or not. ${IsDynamicMemoryEnabled}, [Parameter()] [ValidateSet("true" , "false")] [ArgumentCompleter( { "true" , "false" })] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies whether to migrate the server as an Azure Arc-enabled VM. When set to 'true', an Azure Arc-enabled machine resource with the same name as -TargetVMName must already exist in the resource group specified by -TargetResourceGroupId. ${MigrateAsArcVM}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.Int64] # Specifies the target RAM size in MB. ${TargetVMRam}, [Parameter(ParameterSetName = 'ByIdPowerUser', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.AzLocalDiskInput[]] # Specifies the disks on the source server to be included for replication. ${DiskToInclude}, [Parameter(ParameterSetName = 'ByIdPowerUser', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.AzLocalNicInput[]] # Specifies the NICs on the source server to be included for replication. ${NicToInclude}, [Parameter(Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the target resource group ARM ID where the migrated VM resources will reside. ${TargetResourceGroupId}, [Parameter(Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the name of the VM to be created. ${TargetVMName}, [Parameter(ParameterSetName = 'ByIdDefaultUser', Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the operating system disk for the source server to be migrated. ${OSDiskID}, [Parameter(Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the source appliance name for the AzLocal scenario. ${SourceApplianceName}, [Parameter(Mandatory)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [System.String] # Specifies the target appliance name for the AzLocal scenario. ${TargetApplianceName}, [Parameter()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Path')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Runtime.DefaultInfo(Script = '(Get-AzContext).Subscription.Id')] [System.String] # Azure Subscription ID. ${SubscriptionId}, [Parameter()] [Alias('AzureRMContext', 'AzureCredential')] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Azure')] [System.Management.Automation.PSObject] # The credentials, account, tenant, and subscription used for communication with Azure. ${DefaultProfile}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Wait for .NET debugger to attach ${Break}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be appended to the front of the pipeline ${HttpPipelineAppend}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Runtime.SendAsyncStep[]] # SendAsync Pipeline Steps to be prepended to the front of the pipeline ${HttpPipelinePrepend}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [System.Uri] # The URI for the proxy server to use ${Proxy}, [Parameter(DontShow)] [ValidateNotNull()] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [System.Management.Automation.PSCredential] # Credentials for a proxy server to use for the remote call ${ProxyCredential}, [Parameter(DontShow)] [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Category('Runtime')] [System.Management.Automation.SwitchParameter] # Use the default credentials for the proxy ${ProxyUseDefaultCredentials} ) process { $helperPath = [System.IO.Path]::Combine($PSScriptRoot, "Helper", "AzLocalCommonSettings.ps1") Import-Module $helperPath $helperPath = [System.IO.Path]::Combine($PSScriptRoot, "Helper", "AzLocalCommonHelper.ps1") Import-Module $helperPath CheckResourceGraphModuleDependency CheckResourcesModuleDependency $HasTargetVMCPUCore = $PSBoundParameters.ContainsKey('TargetVMCPUCore') $HasIsDynamicMemoryEnabled = $PSBoundParameters.ContainsKey('IsDynamicMemoryEnabled') if ($HasIsDynamicMemoryEnabled) { $isDynamicRamEnabled = [System.Convert]::ToBoolean($IsDynamicMemoryEnabled) } $HasMigrateAsArcVM = $PSBoundParameters.ContainsKey('MigrateAsArcVM') if ($HasMigrateAsArcVM) { $migrateAsArcVMEnabled = [System.Convert]::ToBoolean($MigrateAsArcVM) } $HasTargetVMRam = $PSBoundParameters.ContainsKey('TargetVMRam') $HasTargetVirtualSwitchId = $PSBoundParameters.ContainsKey('TargetVirtualSwitchId') $HasTargetTestVirtualSwitchId = $PSBoundParameters.ContainsKey('TargetTestVirtualSwitchId') $parameterSet = $PSCmdlet.ParameterSetName # Remove initial command parameters $null = $PSBoundParameters.Remove('MachineId') $null = $PSBoundParameters.Remove('TargetStoragePathId') $null = $PSBoundParameters.Remove('TargetVMCPUCore') $null = $PSBoundParameters.Remove('TargetVirtualSwitchId') $null = $PSBoundParameters.Remove('TargetTestVirtualSwitchId') $null = $PSBoundParameters.Remove('IsDynamicMemoryEnabled') $null = $PSBoundParameters.Remove('MigrateAsArcVM') $null = $PSBoundParameters.Remove('TargetVMRam') $null = $PSBoundParameters.Remove('DiskToInclude') $null = $PSBoundParameters.Remove('NicToInclude') $null = $PSBoundParameters.Remove('TargetResourceGroupId') $null = $PSBoundParameters.Remove('TargetVMName') $null = $PSBoundParameters.Remove('OSDiskID') $null = $PSBoundParameters.Remove('SourceApplianceName') $null = $PSBoundParameters.Remove('TargetApplianceName') # Set common ErrorVariable and ErrorAction for get behaviors $null = $PSBoundParameters.Add('ErrorVariable', 'notPresent') $null = $PSBoundParameters.Add('ErrorAction', 'SilentlyContinue') # Validate ARM ID format from inputs if (!(Test-AzureResourceIdFormat -Data $MachineId -Format $IdFormats.MachineArmIdTemplate)) { throw New-InvalidResourceIdProvidedException ` -ResourceId $MachineId ` -ResourceType "DiscoveredMachine" ` -Format $IdFormats.MachineArmIdTemplate } if (!(Test-AzureResourceIdFormat -Data $TargetStoragePathId -Format $IdFormats.StoragePathArmIdTemplate)) { throw New-InvalidResourceIdProvidedException ` -ResourceId $TargetStoragePathId ` -ResourceType "StorageContainer" ` -Format $IdFormats.StoragePathArmIdTemplate } if (!(Test-AzureResourceIdFormat -Data $TargetResourceGroupId -Format $IdFormats.ResourceGroupArmIdTemplate)) { throw New-InvalidResourceIdProvidedException ` -ResourceId $TargetResourceGroupId ` -ResourceType "ResourceGroup" ` -Format $IdFormats.ResourceGroupArmIdTemplate } if ($HasTargetVirtualSwitchId -and !(Test-AzureResourceIdFormat -Data $TargetVirtualSwitchId -Format $IdFormats.LogicalNetworkArmIdTemplate)) { throw New-InvalidResourceIdProvidedException ` -ResourceId $TargetVirtualSwitchId ` -ResourceType "LogicalNetwork" ` -Format $IdFormats.LogicalNetworkArmIdTemplate } if ($HasTargetTestVirtualSwitchId -and !(Test-AzureResourceIdFormat -Data $TargetTestVirtualSwitchId -Format $IdFormats.LogicalNetworkArmIdTemplate)) { throw New-InvalidResourceIdProvidedException ` -ResourceId $TargetTestVirtualSwitchId ` -ResourceType "LogicalNetwork" ` -Format $IdFormats.LogicalNetworkArmIdTemplate } # Validate TargetVMName (pure parameter check; hoisted up-front so we fail fast # before doing any source/target discovery work). if ($TargetVMName.length -gt 64 -or $TargetVMName.length -eq 0) { throw "The target virtual machine name must be between 1 and 64 characters long." } elseif ($TargetVMName -notmatch "^[^_\W][a-zA-Z0-9\-]{0,63}(?<![-._])$") { throw "The target virtual machine name must begin with a letter or number, and can contain only letters, numbers, or hyphens(-). The names cannot contain special characters \/""[]:|<>+=;,?*@&, whitespace, or begin with '_' or end with '.' or '-'." } elseif (IsReservedOrTrademarked($TargetVMName)) { throw "The target virtual machine name '$TargetVMName' or part of the name is a trademarked or reserved word." } # Arc-enabled VM reuse preflight (only when -MigrateAsArcVM "true"). # Verifies that the customer-onboarded Arc machine exists at the expected # target ARM tuple and has a reusable Kind. Performed up-front so the # cmdlet fails fast on Arc misconfig before any discovery work. if ($HasMigrateAsArcVM -and $migrateAsArcVMEnabled) { # Az.ConnectedMachine is a conditional dependency required only for the Arc # reuse preflight. Fail fast with an actionable install hint if it's missing. CheckConnectedMachineModuleDependency # $TargetResourceGroupId is "/subscriptions/{sub}/resourceGroups/{rg}". # Format already validated above by Test-AzureResourceIdFormat. $targetRgIdArray = $TargetResourceGroupId.Split("/") $targetSubId = $targetRgIdArray[2] $targetRgName = $targetRgIdArray[4] Test-ArcMachineReuseEligibility ` -TargetSubscriptionId $targetSubId ` -TargetResourceGroupName $targetRgName ` -TargetVMName $TargetVMName } # $MachineId is in the format of # "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OffAzure/{2}/{3}/machines/{4}" $MachineIdArray = $MachineId.Split("/") $ResourceGroupName = $MachineIdArray[4] # {1} $SiteType = $MachineIdArray[7] # {2} $SiteName = $MachineIdArray[8] # {3} $MachineName = $MachineIdArray[10] # {4} # Get the source site and the discovered machine if ($SiteType -eq $SiteTypes.HyperVSites) { $instanceType = $AzLocalInstanceTypes.HyperVToAzLocal # Get Hyper-V site with ResourceGroupName, SiteName $null = $PSBoundParameters.Add('ResourceGroupName', $ResourceGroupName) $null = $PSBoundParameters.Add('SiteName', $SiteName) $siteObject = Az.Migrate.Internal\Get-AzMigrateHyperVSite @PSBoundParameters if ($null -eq $siteObject) { throw New-AzMigrateSiteNotFoundException ` -Name $SiteName ` -ResourceGroupName $ResourceGroupName ` -SiteType $SiteType } # Get Hyper-V machine with ResourceGroupName, SiteName, MachineName $null = $PSBoundParameters.Add('MachineName', $MachineName) $machine = Az.Migrate.Internal\Get-AzMigrateHyperVMachine @PSBoundParameters if ($null -eq $machine) { throw New-AzMigrateDiscoveredMachineNotFoundException ` -Name $MachineName ` -ResourceGroupName $ResourceGroupName ` -SiteName $SiteName } $null = $PSBoundParameters.Remove('MachineName') # Get RunAsAccount if (![string]::IsNullOrEmpty($machine.HostId)) { # machine is on a single Hyper-V host $hostIdArray = $machine.HostId.Split("/") if ($hostIdArray.Length -lt 11) { throw "Invalid Hyper-V Host ARM ID '$($machine.HostId)'" } $hostResourceGroupName = $hostIdArray[4] $hostSiteName = $hostIdArray[8] $hostName = $hostIdArray[10] # Get Hyper-V host with ResourceGroupName, SiteName, HostName $null = $PSBoundParameters.Add('HostName', $hostName) $hyperVHost = Az.Migrate.Internal\Get-AzMigrateHyperVHost @PSBoundParameters if ($null -eq $hyperVHost) { throw New-OffAzureResourceNotFoundException ` -Scenario "HyperV" ` -Type "Host" ` -Name $hostName ` -ResourceGroupName $hostResourceGroupName ` -SiteName $hostSiteName } $null = $PSBoundParameters.Remove('HostName') $runAsAccountId = $hyperVHost.RunAsAccountId } elseif(![string]::IsNullOrEmpty($machine.ClusterId)) { # machine is on a Hyper-V cluster $clusterIdArray = $machine.ClusterId.Split("/") if ($clusterIdArray.Length -lt 11) { throw "Invalid Hyper-V Cluster ARM ID '$($machine.ClusterId)'" } $clusterResourceGroupName = $clusterIdArray[4] $clusterSiteName = $clusterIdArray[8] $clusterName = $clusterIdArray[10] # Get Hyper-V cluster with ResourceGroupName, SiteName, ClusterName $null = $PSBoundParameters.Add('ClusterName', $clusterName) $hyperVCluster = Az.Migrate.Internal\Get-AzMigrateHyperVCluster @PSBoundParameters if ($null -eq $hyperVCluster) { throw throw New-OffAzureResourceNotFoundException ` -Scenario "HyperV" ` -Type "Cluster" ` -Name $clusterName ` -ResourceGroupName $clusterResourceGroupName ` -SiteName $clusterSiteName } $null = $PSBoundParameters.Remove('ClusterName') $runAsAccountId = $hyperVCluster.RunAsAccountId } } elseif ($SiteType -eq $SiteTypes.VMwareSites) { $instanceType = $AzLocalInstanceTypes.VMwareToAzLocal # Get VMware site with ResourceGroupName, SiteName $null = $PSBoundParameters.Add('ResourceGroupName', $ResourceGroupName) $null = $PSBoundParameters.Add('SiteName', $SiteName) $siteObject = Az.Migrate.private\Get-AzMigrateSite_Get @PSBoundParameters if ($null -eq $siteObject) { throw New-AzMigrateSiteNotFoundException ` -Name $SiteName ` -ResourceGroupName $ResourceGroupName ` -SiteType $SiteType } # Get VMware machine with ResourceGroupName, SiteName, MachineName $null = $PSBoundParameters.Add('MachineName', $MachineName) $machine = Az.Migrate.Internal\Get-AzMigrateMachine @PSBoundParameters if ($null -eq $machine) { throw New-AzMigrateDiscoveredMachineNotFoundException ` -Name $MachineName ` -ResourceGroupName $ResourceGroupName ` -SiteName $SiteName } $null = $PSBoundParameters.Remove('MachineName') # Get RunAsAccount if (![string]::IsNullOrEmpty($machine.VCenterId)) { # machine is on a single vCenter $vCenterIdArray = $machine.VCenterId.Split("/") if ($vCenterIdArray.Length -lt 11) { throw "Invalid VMware vCenter ARM ID '$($machine.VCenterId)'" } $vCenterResourceGroupName = $vCenterIdArray[4] $vCenterSiteName = $vCenterIdArray[8] $vCenterName = $vCenterIdArray[10] # Get VMware vCenter with ResourceGroupName, SiteName, Name $null = $PSBoundParameters.Add('Name', $vCenterName) $vmwareVCenter = Az.Migrate.Internal\Get-AzMigrateVCenter @PSBoundParameters if ($null -eq $vmwareVCenter) { throw New-OffAzureResourceNotFoundException ` -Scenario "VMware" ` -Type "VCenter" ` -Name $vCenterName ` -ResourceGroupName $vCenterResourceGroupName ` -SiteName $vCenterSiteName } $null = $PSBoundParameters.Remove('Name') $runAsAccountId = $vmwareVCenter.RunAsAccountId } } else { throw "Site type of '$SiteType' in -MachineId is not supported. Only '$($SiteTypes.HyperVSites)' and '$($SiteTypes.VMwareSites)' are supported." } if ([string]::IsNullOrEmpty($runAsAccountId)) { throw "Unable to determine RunAsAccount for site '$SiteName' from machine '$MachineName'. Please verify your appliance setup and provided -MachineId." } $null = $PSBoundParameters.Remove('SiteName') # $siteObject is not null or exception would have been thrown $ProjectName = $siteObject.DiscoverySolutionId.Split("/")[8] # Get Data Replication Service (AMH solution) with ResourceGroupName, Name, MigrateProjectName $amhSolutionName = $AzMigrateSolutions.DataReplicationSolution $null = $PSBoundParameters.Add('MigrateProjectName', $ProjectName) $null = $PSBoundParameters.Add('Name', $amhSolutionName) $amhSolution = Az.Migrate.private\Get-AzMigrateSolution_Get @PSBoundParameters if ($null -eq $amhSolution) { throw New-AzMigrateSolutionNotFoundException ` -Name $amhSolutionName ` -ResourceGroupName $ResourceGroupName ` -ProjectName $ProjectName } $null = $PSBoundParameters.Remove('Name') $null = $PSBoundParameters.Remove('MigrateProjectName') # Validate replication vault $vaultId = $amhSolution.DetailExtendedDetail["vaultId"] $vaultIdArray = $vaultId.Split("/") if ($vaultIdArray.Length -lt 9) { throw New-ReplicationVaultNotFoundInAMHSolutionException -VaultId $vaultId } $replicationVaultName = $vaultIdArray[8] # Get replication vault with ResourceGroupName, Name $null = $PSBoundParameters.Add('Name', $replicationVaultName) $replicationVault = Az.Migrate.Internal\Get-AzMigrateVault @PSBoundParameters if ($null -eq $replicationVault) { throw "No Replication Vault '$replicationVaultName' found in Resource Group '$ResourceGroupName'. Please verify your Azure Migrate project setup." } elseif ($replicationVault.Property.ProvisioningState -ne [ProvisioningState]::Succeeded) { throw "The Replication Vault '$replicationVaultName' is not in a valid state. The provisioning state is '$($replicationVault.Property.ProvisioningState)'. Please verify your Azure Migrate project setup." } $null = $PSBoundParameters.Remove('Name') # Validate replication prequisites Test-ReplicationPrequisites ` -ResourceGroupName $ResourceGroupName ` -VaultName $replicationVaultName ` -ProtectedItemName $MachineName ` -MigrationType $instanceType # Access Discovery Service $discoverySolutionName = $AzMigrateSolutions.DiscoverySolution # Get Discovery Solution with ResourceGroupName, Name, MigrateProjectName $null = $PSBoundParameters.Add('Name', $discoverySolutionName) $null = $PSBoundParameters.Add('MigrateProjectName', $ProjectName) $discoverySolution = Az.Migrate.private\Get-AzMigrateSolution_Get @PSBoundParameters if ($null -eq $discoverySolution) { throw New-AzMigrateSolutionNotFoundException ` -Name $discoverySolutionName ` -ResourceGroupName $ResourceGroupName ` -ProjectName $ProjectName } $null = $PSBoundParameters.Remove('MigrateProjectName') $null = $PSBoundParameters.Remove('Name') # Get Appliances Mapping $appMap = @{} if ($null -ne $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV2"]) { $appMapV2 = $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV2"] | ConvertFrom-Json # Fetch all appliance from V2 map first. Then these can be updated if found again in V3 map. foreach ($item in $appMapV2) { $appMap[$item.ApplianceName.ToLower()] = $item.SiteId } } if ($null -ne $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV3"]) { $appMapV3 = $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV3"] | ConvertFrom-Json foreach ($item in $appMapV3) { $t = $item.psobject.properties $appMap[$t.Name.ToLower()] = $t.Value.SiteId } } if ($null -eq $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV2"] -And $null -eq $discoverySolution.DetailExtendedDetail["applianceNameToSiteIdMapV3"] ) { throw "Server Discovery Solution missing Appliance Details. Invalid Solution." } $hyperVSiteTypeRegex = "(?<=/Microsoft.OffAzure/HyperVSites/).*$" $vmwareSiteTypeRegex = "(?<=/Microsoft.OffAzure/VMwareSites/).*$" # Validate SourceApplianceName & TargetApplianceName $sourceSiteId = $appMap[$SourceApplianceName.ToLower()] $targetSiteId = $appMap[$TargetApplianceName.ToLower()] if (-not ($sourceSiteId -match $hyperVSiteTypeRegex -and $targetSiteId -match $hyperVSiteTypeRegex) -and -not ($sourceSiteId -match $vmwareSiteTypeRegex -and $targetSiteId -match $hyperVSiteTypeRegex)) { throw "Error encountered in matching the given source appliance name '$SourceApplianceName' and target appliance name '$TargetApplianceName'. Please verify the VM site type to be either for HyperV or VMware for both source and target appliances, and the appliance names are correct." } # Get healthy asrv2 fabrics in the resource group # Get all fabrics with ResourceGroupName $allFabrics = Az.Migrate.private\Get-AzMigrateLocalReplicationFabric_List1 @PSBoundParameters ` | Where-Object { $_.Property.ProvisioningState -eq [ProvisioningState]::Succeeded -and $_.Property.CustomProperty.MigrationSolutionId -eq $amhSolution.Id } # Filter for source fabric if ($instanceType -eq $AzLocalInstanceTypes.HyperVToAzLocal) { $fabricInstanceType = $FabricInstanceTypes.HyperVInstance } else { # $instanceType -eq $AzLocalInstanceTypes.VMwareToAzLocal $fabricInstanceType = $FabricInstanceTypes.VmwareInstance } $sourceFabric = $allFabrics | Where-Object { $_.Property.CustomProperty.InstanceType -ceq $fabricInstanceType -and $_.Name.StartsWith($SourceApplianceName, [System.StringComparison]::InvariantCultureIgnoreCase) } if ($null -eq $sourceFabric) { throw "Couldn't find connected source appliance with the name '$SourceApplianceName'. Deploy a source appliance by completing the Discover step of migration for your on-premises environment." } # Get source fabric agent (dra) with ResourceGroupName, FabricName $null = $PSBoundParameters.Add('FabricName', $sourceFabric.Name) $sourceDras = Az.Migrate.Internal\Get-AzMigrateFabricAgent @PSBoundParameters $sourceDra = $sourceDras | Where-Object { $_.Property.MachineName -eq $SourceApplianceName -and $_.Property.CustomProperty.InstanceType -eq $fabricInstanceType -and $_.Property.IsResponsive -eq $true } if ($null -eq $sourceDra) { throw "The source appliance '$SourceApplianceName' is in a disconnected state. Ensure that the source appliance is running and has connectivity before proceeding." } $null = $PSBoundParameters.Remove('FabricName') $sourceDra = $sourceDra[0] # Filter for target fabric $fabricInstanceType = $FabricInstanceTypes.AzLocalInstance $targetFabric = $allFabrics | Where-Object { $_.Property.CustomProperty.InstanceType -ceq $fabricInstanceType -and $_.Name.StartsWith($TargetApplianceName, [System.StringComparison]::InvariantCultureIgnoreCase) } if ($null -eq $targetFabric) { throw "Couldn't find connected target appliance with the name '$TargetApplianceName'. Deploy a target appliance by completing the Configuration step of migration for your Azure Local environment." } # Get target fabric agent (dra) with ResourceGroupName, FabricName $null = $PSBoundParameters.Add('FabricName', $targetFabric.Name) $targetDras = Az.Migrate.Internal\Get-AzMigrateFabricAgent @PSBoundParameters $targetDra = $targetDras | Where-Object { $_.Property.MachineName -eq $TargetApplianceName -and $_.Property.CustomProperty.InstanceType -eq $fabricInstanceType -and $_.Property.IsResponsive -eq $true } if ($null -eq $targetDra) { "The target appliance '$TargetApplianceName' is in a disconnected state. Ensure that the target appliance is running and has connectivity before proceeding." } $null = $PSBoundParameters.Remove('FabricName') $targetDra = $targetDras[0] # Validate Policy $policyName = $replicationVaultName + $instanceType + "policy" # Get replication policy with ResourceGroupName, Name, VaultName $null = $PSBoundParameters.Add('Name', $policyName) $null = $PSBoundParameters.Add('VaultName', $replicationVaultName) $policy = Az.Migrate.Internal\Get-AzMigratePolicy @PSBoundParameters if ($null -eq $policy) { throw "The replication policy '$policyName' not found. The replication infrastructure is not initialized. Run the Initialize-AzMigrateLocalReplicationInfrastructure command." } elseif ($policy.Property.ProvisioningState -ne [ProvisioningState]::Succeeded) { throw "The replication policy '$policyName' is not in a valid state. The provisioning state is '$($policy.Property.ProvisioningState)'. Re-run the Initialize-AzMigrateLocalReplicationInfrastructure command." } $null = $PSBoundParameters.Remove('Name') # Validate Replication Extension $replicationExtensionName = ($sourceFabric.Id -split '/')[-1] + "-" + ($targetFabric.Id -split '/')[-1] + "-MigReplicationExtn" # Get replication extension with ResourceGroupName, Name, VaultName $null = $PSBoundParameters.Add('Name', $replicationExtensionName) $replicationExtension = Az.Migrate.Internal\Get-AzMigrateReplicationExtension @PSBoundParameters if ($null -eq $replicationExtension) { throw "The replication extension '$replicationExtensionName' not found. The replication infrastructure is not initialized. Run the Initialize-AzMigrateLocalReplicationInfrastructure command." } elseif ($replicationExtension.Property.ProvisioningState -ne [ProvisioningState]::Succeeded) { throw "The replication extension '$replicationExtensionName' is not in a valid state. The provisioning state is '$($replicationExtension.Property.ProvisioningState)'. Re-run the Initialize-AzMigrateLocalReplicationInfrastructure command." } $null = $PSBoundParameters.Remove('Name') # Get ARC Resource Bridge info $targetClusterId = $targetFabric.Property.CustomProperty.Cluster.ResourceName $targetClusterIdArray = $targetClusterId.Split("/") $targetSubscription = $targetClusterIdArray[2] $arbArgQuery = GetARGQueryForArcResourceBridge -HCIClusterID $targetClusterId $arbArgResult = Az.ResourceGraph\Search-AzGraph -Query $arbArgQuery -Subscription $targetSubscription if ($null -eq $arbArgResult) { throw "$($ArcResourceBridgeValidationMessages.NoClusters). Validate target cluster with id '$targetClusterId' exists." } elseif ($arbArgResult.statusOfTheBridge -ne "Running") { throw "$($ArcResourceBridgeValidationMessages.NotRunning). Make sure the Arc Resource Bridge is online before retrying." } # Construct create protected item request object $protectedItemProperties = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.ProtectedItemModelProperties]::new() $protectedItemProperties.PolicyName = $policyName $protectedItemProperties.ReplicationExtensionName = $replicationExtensionName if ($SiteType -eq $SiteTypes.HyperVSites) { $customProperties = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.HyperVToAzStackHCIProtectedItemModelCustomProperties]::new() $isSourceDynamicMemoryEnabled = $machine.IsDynamicMemoryEnabled } elseif ($SiteType -eq $SiteTypes.VMwareSites) { $customProperties = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.VMwareToAzStackHCIProtectedItemModelCustomProperties]::new() $isSourceDynamicMemoryEnabled = $false } $customProperties.InstanceType = $instanceType $customProperties.CustomLocationRegion = $arbArgResult.CustomLocationRegion $customProperties.FabricDiscoveryMachineId = $machine.Id $customProperties.RunAsAccountId = $runAsAccountId $customProperties.SourceFabricAgentName = $sourceDra.Name # Validate storage path exists and is in a usable state $storagePath = Get-AzResource ` -ResourceId $TargetStoragePathId ` -ErrorVariable notPresent ` -ErrorAction SilentlyContinue if ($null -eq $storagePath) { throw "Storage path with Id '$TargetStoragePathId' not found. Please provide a valid storage path ARM ID." } # Creation must have succeeded for the storage path to be usable $creationStatus = $storagePath.Properties.status.provisioningStatus.status if ([string]::IsNullOrEmpty($creationStatus)) { throw "Storage path '$($storagePath.Name)' creation status is unavailable. Please verify the storage path resource is fully provisioned." } if ($creationStatus -ne "Succeeded") { throw "Storage path '$($storagePath.Name)' has a creation provisioning status of '$creationStatus'. Only storage paths with a successful creation can be used. Please select a different storage path or wait for provisioning to complete." } # The latest operation (ProvisioningState) must also be Succeeded $provisioningState = $storagePath.Properties.provisioningState if ([string]::IsNullOrEmpty($provisioningState)) { throw "Storage path '$($storagePath.Name)' provisioning state is unavailable. Please verify the storage path resource is fully provisioned." } if ($provisioningState -ne "Succeeded") { throw "Storage path '$($storagePath.Name)' has a provisioning state of '$provisioningState'. Only storage paths with a 'Succeeded' provisioning state can be used. Please resolve the issue or select a different storage path." } $customProperties.StorageContainerId = $TargetStoragePathId $customProperties.TargetArcClusterCustomLocationId = $arbArgResult.CustomLocation $customProperties.TargetFabricAgentName = $targetDra.Name $customProperties.TargetHciClusterId = $targetClusterId $customProperties.TargetResourceGroupId = $TargetResourceGroupId $customProperties.TargetVMName = $TargetVMName $customProperties.IsDynamicRam = if ($HasIsDynamicMemoryEnabled) { $isDynamicRamEnabled } else { $isSourceDynamicMemoryEnabled } if ($HasMigrateAsArcVM) { $customProperties.MigrateAsArcVM = $migrateAsArcVMEnabled } # Determine target VM Hyper-V Generation if ($SiteType -eq $SiteTypes.HyperVSites) { # Hyper-V source $customProperties.HyperVGeneration = $machine.Generation } else { #Vmware source, non-BOIS VMs will be migrated to Gen2 $customProperties.HyperVGeneration = if ($machine.Firmware -ieq "BIOS") { "1" } else { "2" } } # Validate TargetVMCPUCore if ($HasTargetVMCPUCore) { if ($TargetVMCPUCore -lt $TargetVMCPUCores.Min -or $TargetVMCPUCore -gt $TargetVMCPUCores.Max) { throw "Specify -TargetVMCPUCore between $($TargetVMCPUCores.Min) and $($TargetVMCPUCores.Max)." } $customProperties.TargetCpuCore = $TargetVMCPUCore } else { $customProperties.TargetCpuCore = $machine.NumberOfProcessorCore } # Validate TargetVMRam if ($HasTargetVMRam) { if ($customProperties.HyperVGeneration -eq "1") { # Between 512 MB and 1 TB if ($TargetVMRam -lt $TargetVMRamInMB.Gen1Min -or $TargetVMRam -gt $TargetVMRamInMB.Gen1Max) { throw "Specify -TargetVMRAM between $($TargetVMRamInMB.Gen1Min) and $($TargetVMRamInMB.Gen1Max) MB (i.e., 1 TB) for Hyper-V Generation 1 VM." } } else # Hyper-V Generation 2 { # Between 32 MB and 12 TB if ($TargetVMRam -lt $TargetVMRamInMB.Gen2Min -or $TargetVMRam -gt $TargetVMRamInMB.Gen2Max) { throw "Specify -TargetVMRAM between $($TargetVMRamInMB.Gen2Min) and $($TargetVMRamInMB.Gen2Max) MB (i.e., 12 TB) for Hyper-V Generation 2 VM." } } $customProperties.TargetMemoryInMegaByte = $TargetVMRam } else { $customProperties.TargetMemoryInMegaByte = [System.Math]::Max($machine.AllocatedMemoryInMB, $RAMConfig.MinTargetMemoryInMB) } # Construct default dynamic memory config $memoryConfig = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.ProtectedItemDynamicMemoryConfig]::new() $memoryConfig.MinimumMemoryInMegaByte = [System.Math]::Min($customProperties.TargetMemoryInMegaByte, $RAMConfig.DefaultMinDynamicMemoryInMB) $memoryConfig.MaximumMemoryInMegaByte = [System.Math]::Max($customProperties.TargetMemoryInMegaByte, $RAMConfig.DefaultMaxDynamicMemoryInMB) $memoryConfig.TargetMemoryBufferPercentage = $RAMConfig.DefaultTargetMemoryBufferPercentage $customProperties.DynamicMemoryConfig = $memoryConfig # Disks and Nics [PSCustomObject[]]$disks = @() [PSCustomObject[]]$nics = @() if ($parameterSet -match 'DefaultUser') { if ($SiteType -eq $SiteTypes.HyperVSites) { $osDisk = $machine.Disk | Where-Object { $_.InstanceId -eq $OSDiskID } if ($null -eq $osDisk) { throw "No Disk found with InstanceId $OSDiskID from discovered machine disks." } } elseif ($SiteType -eq $SiteTypes.VMwareSites) { $osDisk = $machine.Disk | Where-Object { $_.Uuid -eq $OSDiskID } if ($null -eq $osDisk) { throw "No Disk found with Uuid $OSDiskID from discovered machine disks." } } foreach ($sourceDisk in $machine.Disk) { $diskId = if ($SiteType -eq $SiteTypes.HyperVSites) { $sourceDisk.InstanceId } else { $sourceDisk.Uuid } $diskSize = if ($SiteType -eq $SiteTypes.HyperVSites) { $sourceDisk.MaxSizeInByte } else { $sourceDisk.MaxSizeInBytes } $DiskObject = [PSCustomObject]@{ DiskId = $diskId DiskSizeGb = [long] [Math]::Ceiling($diskSize / 1GB) DiskFileFormat = "VHDX" IsDynamic = $true IsOSDisk = $diskId -eq $OSDiskID } $disks += $DiskObject } foreach ($sourceNic in $machine.NetworkAdapter) { $NicObject = [PSCustomObject]@{ NicId = $sourceNic.NicId TargetNetworkId = $TargetVirtualSwitchId TestNetworkId = if ($HasTargetTestVirtualSwitchId) { $TargetTestVirtualSwitchId } else { $TargetVirtualSwitchId } SelectionTypeForFailover = $VMNicSelection.SelectedByUser } $nics += $NicObject } } else { # PowerUser if ($null -eq $DiskToInclude -or $DiskToInclude.length -eq 0) { throw "Invalid DiskToInclude. At least one disk is required." } # Validate OSDisk is set. $osDisk = $DiskToInclude | Where-Object { $_.IsOSDisk } if (($null -eq $osDisk) -or ($osDisk.length -ne 1)) { throw "Invalid DiskToInclude. One disk must be designated as the OS disk." } # Validate DiskToInclude [PSCustomObject[]]$uniqueDisks = @() foreach ($disk in $DiskToInclude) { # Enforce VHDX for Gen2 VMs if ($customProperties.HyperVGeneration -eq "2" -and $disk.DiskFileFormat -eq "VHD") { throw "Please specify 'VHDX' as Format for the disk with id '$($disk.DiskId)' in -DiskToInclude by re-running New-AzMigrateLocalDiskMappingObject." } # PhysicalSectorSize must be 512 for VHD format if it is set if ($disk.DiskFileFormat -eq "VHD" -and $null -ne $disk.DiskPhysicalSectorSize -and $disk.DiskPhysicalSectorSize -ne 512) { throw "Invalid Physical sector size of $($disk.DiskPhysicalSectorSize) is found for VHD format. Please replace disk with id '$($disk.DiskId)' in -DiskToInclude by re-running New-AzMigrateLocalDiskMappingObject with 512 as -PhysicalSectorSize." } if ($SiteType -eq $SiteTypes.HyperVSites) { $discoveredDisk = $machine.Disk | Where-Object { $_.InstanceId -eq $disk.DiskId } if ($null -eq $discoveredDisk) { throw "No Disk found with InstanceId '$($disk.DiskId)' from discovered machine disks." } } elseif ($SiteType -eq $SiteTypes.VMwareSites) { $discoveredDisk = $machine.Disk | Where-Object { $_.Uuid -eq $disk.DiskId } if ($null -eq $discoveredDisk) { throw "No Disk found with Uuid '$($disk.DiskId)' from discovered machine disks." } } if ($uniqueDisks.Contains($disk.DiskId)) { throw "The disk id '$($disk.DiskId)' is already taken." } $uniqueDisks += $disk.DiskId $htDisk = @{} $disk.PSObject.Properties | ForEach-Object { $htDisk[$_.Name] = $_.Value } $disks += [PSCustomObject]$htDisk } # Validate NicToInclude [PSCustomObject[]]$uniqueNics = @() foreach ($nic in $NicToInclude) { $discoveredNic = $machine.NetworkAdapter | Where-Object { $_.NicId -eq $nic.NicId } if ($null -eq $discoveredNic) { throw "The Nic id '$($nic.NicId)' is not found." } if ($uniqueNics.Contains($nic.NicId)) { throw "The Nic id '$($nic.NicId)' is already included. Please remove the duplicate entry and try again." } $uniqueNics += $nic.NicId $htNic = @{} $nic.PSObject.Properties | ForEach-Object { $htNic[$_.Name] = $_.Value } if ($htNic.SelectionTypeForFailover -eq $VMNicSelection.SelectedByUser -and [string]::IsNullOrEmpty($htNic.TargetNetworkId)) { throw throw "The TargetVirtualSwitchId parameter is required when the CreateAtTarget flag is set to 'true'. NIC '$($htNic.NicId)'. Please utilize the New-AzMigrateLocalNicMappingObject command to properly create a Nic mapping object." } $nics += [PSCustomObject]$htNic } } if ($SiteType -eq $SiteTypes.HyperVSites) { $customProperties.DisksToInclude = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.HyperVToAzStackHCIDiskInput[]]$disks $customProperties.NicsToInclude = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.HyperVToAzStackHCINicInput[]]$nics } elseif ($SiteType -eq $SiteTypes.VMwareSites) { $customProperties.DisksToInclude = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.VMwareToAzStackHCIDiskInput[]]$disks $customProperties.NicsToInclude = [Microsoft.Azure.PowerShell.Cmdlets.Migrate.Models.VMwareToAzStackHCINicInput[]]$nics } $protectedItemProperties.CustomProperty = $customProperties # Remove common ErrorVariable and ErrorAction for get behaviors $null = $PSBoundParameters.Remove('ErrorVariable') $null = $PSBoundParameters.Remove('ErrorAction') if ($PSCmdlet.ShouldProcess($MachineId, "Replicate VM.")) { # Create protected item with ResourceGroupName, VaultName, Name, Property $null = $PSBoundParameters.Add('Name', $MachineName) $null = $PSBoundParameters.Add('Property', $protectedItemProperties) $null = $PSBoundParameters.Add('NoWait', $true) $operation = Az.Migrate.Internal\New-AzMigrateProtectedItem @PSBoundParameters $null = $PSBoundParameters.Remove('NoWait') $null = $PSBoundParameters.Remove('Property') $null = $PSBoundParameters.Remove('Name') # Get job with ResourceGroupName, VaultName, Name $jobName = $operation.Target.Split("/")[-1].Split("?")[0].Split("_")[0] $null = $PSBoundParameters.Add('Name', $jobName) return Az.Migrate.Internal\Get-AzMigrateLocalReplicationJob @PSBoundParameters } } } # SIG # Begin signature block # MIInSAYJKoZIhvcNAQcCoIInOTCCJzUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD5nTjKQNnip//Y # 1qiF72b3y2ORpdYwpVvUmWLTnau5dKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnkMIIZ4AIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOHrde4t # 3ltyfHiLZavqBLEWnPF8oMn+L4DJ+R+kcI8UMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAlF9sJC9/SJixN5kpk/5BLhvQFM6uAwoU97epmR9Q # iggXZUMq9L2gEj6MsAd5/uuqvndaqWEJNXxII0oxq2IAFytpMzF/LYiZJLU8KHbJ # lN92xZNa79pD9Rm0Hz0IT5dXe5FrILrIK4hhNEKn14+Dcj+PhNYFPjZC3itI4XeV # a54qAz5PWuRw4zTrYcHgmEPHgvg+DKE1LqXKGA+BV+Py1crww7Y7ousiULgMUmXt # yAnb7aJ7E8tJmdfaQqLdxmlNSnx286ksqgJg1gVzbIhX+UQi91+IhzBoDRS48+qJ # M3LF1gsvJEK6s/MdhDFE8avV4KPURGtXmFSsXjUJi4g+/KGCF5YwgheSBgorBgEE # AYI3AwMBMYIXgjCCF34GCSqGSIb3DQEHAqCCF28wghdrAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCBQyKoV38kchNrwA0suA+kUDRkUcFvnQlhHz9Xf # v4QJ0QIGaqn4/p9RGBIyMDI2MDkzMDA0MTk0Mi44OFowBIACAfSggdGkgc4wgcsx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNT # IEVTTjo3RjAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgU2VydmljZaCCEe0wggcgMIIFCKADAgECAhMzAAACHqOspG45b3xJAAEAAAIe # MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4X # DTI2MDIxOTE5Mzk0OVoXDTI3MDUxNzE5Mzk0OVowgcsxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNh # IE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo3RjAwLTA1RTAt # RDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIw # DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXROO1sPCxHsV7xpzqiXmzXOG1O # p3YBalyFCEun0bmaZIzbc3l/JAYJDUPTqs4Dc+BcoX7vq9e84KzZWwu/WjCPiYcT # ISqKrwYnnIL79A1hGlk8Dx7s6B6TMM7pL/i/L+NMxhuneuG4WIooLNNY5C10VwX4 # PSTfr0jumb8TTtLI0waS413mWPlIn3VSoW5l+MwHpxDbCHvua2JFRV2PnfKN02qP # 4ZCX5hrPb0GOvOftTWWf4mkuWdvTF0aZmgg8plvAFVxa3Ivi7KEwvtJJOaI59ZdT # 6D7I2XQJ2gsYvwu1YcSLwWy5M95J1KqZ4yu8toSaJtNVNLi9BBjw0+dvq4jnLqI1 # X28EVybwtT+UNOMZOo9rtQFPiB1/kmbfBit8IVng/+PkyipPQk41xrnSO3hMYj3R # KKFdoMRiqTbdLQglndSRSm6QNFOMrvXcEjKR9/HIGox5Cp87TO9Z9THsGuZSm6BB # zD334PEuXaB/65ASlGaeVutUn129b12zh+oQ83aMbRDAXU8FKCU1xXVKmpkqK1CA # EZLC7/zYArO2gIfBhEdE3DPBNV7/Uo1O+aoB3hSB6zjLA4fTaFpqBPzBhjw51Z2M # qfeTTnbD6SZzRQLQX6JVdMZkgzG+j2IFlChd6HNG1Yn9U60q8LJLdywrM3utK1Yn # CNJbPp205/SX7K0tAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU5goWmuoEHQlmYlwU # Lhw8+Z4XgmQwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0f # BFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwv # TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsG # AQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx # MCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAKShkHk2clUVvnAp6NGi # eTnXxrZME1ikpwEy18voFLQBFoAE4wZguU826EjUfCZ6U/2FfeirdNoSb9wOSTM1 # ADMN50+ChEjZHv7ymg1Ja8dcQCztJk4Ob3HsqqUGQ1kz17HhdjXI2ZU4CZYONGvu # MqNqJBue1/sQLgY2KTEYZpVY6N9i3dD1fSv8qzwoGVvMNH3OMD9MJy1HhyjValTV # lEsWsH1uXx1HGxufJPapDjUTt1PXZHfR4gZTOISzkY37bpX+i9c6LbR0mIzXeFha # /LU00kCGQo6UsHU426d3p9+E91Rwday7xX6VHRpqQxXrgeoNsu6ZmsI3BSh9XHfE # yTwXi0Jgm1DEtPLBzfSxkAPVLawLX3n3HoqLED6njUUtSXyDrigfLdt9icfnF3gk # 4GBChqqd0aNxy3Gv7wSSeOErKuADOtNwosltR7OCjJ7xusIsn7Lo8CgSOldGRJgB # TzB9DdhZFyToAvChXtSKfz6ukZBJteEXpzV1MVqReYKEKW53ggANj+3olGQn7ToX # Mv6MN3wotXxCPvsl+K5OI8gbkb/GWcahkVxf7LIG0O/NkTjx35j4dhR39y+EfUUq # XsAf7kDKi2olIWa8z8G5hHHYHbRqxVeKVXaTYls07csYLPdD52kSXPCx8muRrU3+ # B62Zrt9amjCw2+ghoRC+Np3xMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAA # AAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUg # QXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQAD # ggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2 # AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpS # g0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2r # rPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k # 45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSu # eik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09 # /SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR # 6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxC # aC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaD # IV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMUR # HXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMB # AAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQq # p1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ # 6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRt # MBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBB # MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP # 6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWlj # cm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2 # LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMu # Y3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2 # Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03d # mLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1Tk # eFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kp # icO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKp # W99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrY # UP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QB # jloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkB # RH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0V # iY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq # 0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1V # M1izoXBm8qGCA1AwggI4AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP # cGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5 # NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAH # BgUrDgMCGgMVAIP9A2QoMhbhUgXuPeiLaputHRr/oIGDMIGApH4wfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDuZupUMCIYDzIw # MjYwOTMwMDE1NDI4WhgPMjAyNjEwMDEwMTU0MjhaMHcwPQYKKwYBBAGEWQoEATEv # MC0wCgIFAO5m6lQCAQAwCgIBAAICJxICAf8wBwIBAAICE9EwCgIFAO5oO9QCAQAw # NgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgC # AQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEAQu34v20O9/L5lCWPreLdsM95+ryi # rnoEF2gzPjSIZ8ZIf8bQSDKUDxAfPeJYDayzdYnPChU0tJVasvJublj1fE8rA0Yq # kd22AndW7jbEx1D0JzuXxdNPOKP0QjDNncvz8yNXtT74YzZCk51K4u8QGUfi1Nwn # UcUlDYz0emtGtqof/b+DYZXL9TK94P/mcvIk8/NFsjR8dfpTKCAsBruNr/lmYG/6 # aHnefOzzklZzf2ydBAOI2YB62U9n29iyDDmMtmUcCGxIxbJP9Cj9sIxktcXbK9Kl # j6JuIxp6yNwRF0syzA3a60jFNXyNMiK5P0UrRWAxJ+X/MrFwvu2VmOtrajGCBA0w # ggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACHqOs # pG45b3xJAAEAAAIeMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYL # KoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEID5ZFabpyyor7s5lkX6zgEkYhD8G # ZuIvGXFIL7bACp5nMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgL4FdavP2 # B4yAzwG+fxurEeOEdcnb0QGLMhMjDQH284IwgZgwgYCkfjB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMAITMwAAAh6jrKRuOW98SQABAAACHjAiBCDX6i+SUPcY # QW6nCX5NEG05gYh5rRPOPdqGfzHyZ0ZzUDANBgkqhkiG9w0BAQsFAASCAgAwXjNX # 7DrCOwsGs2ML1dZ3xcr5KYRFr41y3rCpIeaPncgrwLnm0jMXyPByp1+9S9ECBDDv # FZFVADlfNJEp1MTIVwByEwl9UovjPuamXQQqYALuin2ewPqJAU5gMZsiV4BP8iSK # fLzSvWIw8gUaaGvpCeGx6GbaTolbJ29pOhGCpXEtzvG0bmHxgJjwx0un9ZojVX+c # eMalzHp/bU+Su7HgegGTNS17IpGO8EuEuUkkmYujKPUgS26d511NsqPwKqo1Q1ZQ # Da3ZxzSIOQ6ruv60c+JcYB5AU+SpNeDpzBAC1NfYCXZI6WL+XbqO8Aa4qUab9tdt # SUfViU6qJXjCfh7Gq0WL6lPeMxJQPzOXYPYTvNMxSder4DHiG9/Rk3NFbwRKkA75 # lVOyCvDRTIoIj2C1eoC0joVfCJC9ytb4KzD3wf9yy6UCa9edLJT3mjce2Khxadde # bMkO+LwZn5n1iuQwXOP9C4uISAiqM5RbfwJJr+f4t1xGWrTtkLjXszLgc3yhvv+i # YlNeZMApKddJi1GV3Z+0TXuxirz826kQvRvfzNpPG623GmtM5vjSYXwhteh01voj # O44WmK4u6H9LWRSOgCJxmv/2X3iGQg0iMKMJgV6VaLJWDNAz+hr8iV/X6/eio3om # o3/vZwqVXHKrRA7md3jd068R3l4+OjsHX0QVrg== # SIG # End signature block |