Automatická aktualizace Mobility v replikaci z Azure do Azure

Azure Site Recovery používá měsíční četnost vydávání verzí k opravě problémů a vylepšení stávajících funkcí nebo přidání nových funkcí. Pokud chcete zůstat aktuální se službou, musíte naplánovat nasazení oprav každý měsíc. Abyste se vyhnuli režijním nákladům spojeným s každým upgradem, můžete službě Site Recovery povolit správu aktualizací součástí.

Jak je uvedeno v architektuře zotavení po havárii Azure do Azure, Mobility se nainstaluje na všechny virtuální počítače Azure, které mají povolenou replikaci z jedné oblasti Azure do jiné. Když používáte automatické aktualizace, každá nová verze aktualizuje rozšíření Mobility.

Poznámka:

Při práci s Azure doporučujeme používat modul Azure Az PowerShellu. Začněte tím, že si projdete téma Instalace Azure PowerShellu. Informace o tom, jak migrovat na modul Az PowerShell, najdete v tématu Migrace Azure PowerShellu z AzureRM na Az.

Jak fungují automatické aktualizace

Když ke správě aktualizací použijete Site Recovery, nasadí globální runbook (používaný službami Azure) prostřednictvím účtu Automation vytvořeného ve stejném předplatném jako trezor. Každý trezor používá jeden účet Automation. Pro každý virtuální počítač v trezoru runbook kontroluje aktivní automatické aktualizace. Pokud je k dispozici novější verze rozšíření Mobility, nainstaluje se aktualizace.

Výchozí plán runbooku probíhá každý den v 12:00 v časovém pásmu zeměpisné oblasti replikovaného virtuálního počítače. Plán runbooku můžete také změnit prostřednictvím účtu Automation.

Poznámka:

Počínaje kumulativní aktualizací 35 můžete zvolit existující účet Automation, který se má použít pro aktualizace. Před kumulativní aktualizací 35 služba Site Recovery ve výchozím nastavení vytvořila účet Automation. Tuto možnost můžete vybrat jenom v případě, že povolíte replikaci pro virtuální počítač. Není k dispozici pro virtuální počítač, který už má povolenou replikaci. Vybrané nastavení platí pro všechny virtuální počítače Azure chráněné ve stejném trezoru.

Zapnutí automatických aktualizací nevyžaduje restartování virtuálních počítačů Azure ani nemá vliv na probíhající replikaci.

Fakturace úloh v účtu Automation vychází z počtu minut modulu runtime úloh použitých v měsíci. Provádění úlohy trvá několik sekund přibližně minutu každý den a je pokryto jako volné jednotky. Ve výchozím nastavení je 500 minut součástí bezplatných jednotek pro účet Automation, jak je znázorněno v následující tabulce:

Zahrnuté bezplatné jednotky (každý měsíc) Cena
Modul runtime úlohy 500 minut {0,14/minuta

Povolit automatické aktualizace

Existuje několik způsobů, jak může Site Recovery spravovat aktualizace rozšíření:

Správa v rámci kroku povolení replikace

Když povolíte replikaci virtuálního počítače buď ze zobrazení virtuálního počítače, nebo z trezoru služby Recovery Services, můžete site Recovery povolit správu aktualizací pro rozšíření Site Recovery nebo ho spravovat ručně.

Extension settings

Přepnutí nastavení aktualizace rozšíření v trezoru

  1. V trezoru služby Recovery Services přejděte do části Správa>infrastruktury Site Recovery.

  2. V části Aktualizace rozšíření služby Azure Virtual Machines>Nastavení> Spravovat Site Recovery výběrem možnosti Zapnuto.

    Pokud chcete rozšíření spravovat ručně, vyberte Vypnuto.

    Důležité

    Když zvolíte Povolit site Recovery ke správě, použije se nastavení pro všechny virtuální počítače v trezoru.

  3. Zvolte Uložit.

Extension update settings

Poznámka:

Obě možnosti vás upozorní na účet Automation používaný ke správě aktualizací. Pokud tuto funkci používáte v trezoru poprvé, vytvoří se ve výchozím nastavení nový účet Automation. Můžete také přizpůsobit nastavení a zvolit existující účet Automation. Po definování budou všechny následné akce umožňující replikaci ve stejném trezoru používat vybraný účet Automation. V současné době rozevírací nabídka vypíše pouze účty automation, které jsou ve stejné skupině prostředků jako trezor.

Pro vlastní účet Automation použijte následující skript:

Důležité

V kontextu účtu Automation spusťte následující skript. Tento skript využívá spravované identity přiřazené systémem jako typ ověřování.

param(
    [Parameter(Mandatory=$true)]
    [String] $VaultResourceId,
    [Parameter(Mandatory=$true)]
    [ValidateSet("Enabled",'Disabled')]
    [Alias("Enabled or Disabled")]
    [String] $AutoUpdateAction,
    [Parameter(Mandatory=$false)]
    [String] $AutomationAccountArmId
)
$SiteRecoveryRunbookName = "Modify-AutoUpdateForVaultForPatner"
$TaskId = [guid]::NewGuid().ToString()
$SubscriptionId = "00000000-0000-0000-0000-000000000000"
$AsrApiVersion = "2021-12-01"
$ArmEndPoint = "https://management.azure.com"
$AadAuthority = "https://login.windows.net/"
$AadAudience = "https://management.core.windows.net/"
$AzureEnvironment = "AzureCloud"
$Timeout = "160"
$AuthenticationType = "SystemAssignedIdentity"
function Throw-TerminatingErrorMessage
{
        Param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $Message
        )
    throw ("Message: {0}, TaskId: {1}.") -f $Message, $TaskId
}
function Write-Tracing
{
        Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateSet("Informational", "Warning", "ErrorLevel", "Succeeded", IgnoreCase = $true)]
                [String]
        $Level,
        [Parameter(Mandatory=$true)]
        [String]
        $Message,
            [Switch]
        $DisplayMessageToUser
        )
    Write-Output $Message
}
function Write-InformationTracing
{
        Param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $Message
        )
    Write-Tracing -Message $Message -Level Informational -DisplayMessageToUser
}
function ValidateInput()
{
    try
    {
        if(!$VaultResourceId.StartsWith("/subscriptions", [System.StringComparison]::OrdinalIgnoreCase))
        {
            $ErrorMessage = "The vault resource id should start with /subscriptions."
            throw $ErrorMessage
        }
        $Tokens = $VaultResourceId.SubString(1).Split("/")
        if(!($Tokens.Count % 2 -eq 0))
        {
            $ErrorMessage = ("Odd Number of tokens: {0}." -f $Tokens.Count)
            throw $ErrorMessage
        }
        if(!($Tokens.Count/2 -eq 4))
        {
            $ErrorMessage = ("Invalid number of resource in vault ARM id expected:4, actual:{0}." -f ($Tokens.Count/2))
            throw $ErrorMessage
        }
        if($AutoUpdateAction -ieq "Enabled" -and [string]::IsNullOrEmpty($AutomationAccountArmId))
        {
            $ErrorMessage = ("The automation account ARM id should not be null or empty when AutoUpdateAction is enabled.")
            throw $ErrorMessage
        }
    }
    catch
    {
        $ErrorMessage = ("ValidateInput failed with [Exception: {0}]." -f $_.Exception)
        Write-Tracing -Level ErrorLevel -Message $ErrorMessage -DisplayMessageToUser
        Throw-TerminatingErrorMessage -Message $ErrorMessage
    }
}
function Initialize-SubscriptionId()
{
    try
    {
        $Tokens = $VaultResourceId.SubString(1).Split("/")
        $Count = 0
                $ArmResources = @{}
        while($Count -lt $Tokens.Count)
        {
            $ArmResources[$Tokens[$Count]] = $Tokens[$Count+1]
            $Count = $Count + 2
        }
                return $ArmResources["subscriptions"]
    }
    catch
    {
        Write-Tracing -Level ErrorLevel -Message ("Initialize-SubscriptionId: failed with [Exception: {0}]." -f $_.Exception) -DisplayMessageToUser
        throw
    }
}
function Invoke-InternalRestMethod($Uri, $Headers, [ref]$Result)
{
    $RetryCount = 0
    $MaxRetry = 3
    do
    {
        try
        {
            $ResultObject = Invoke-RestMethod -Uri $Uri -Headers $Headers
            ($Result.Value) += ($ResultObject)
            break
        }
        catch
        {
            Write-InformationTracing ("Retry Count: {0}, Exception: {1}." -f $RetryCount, $_.Exception)
            $RetryCount++
            if(!($RetryCount -le $MaxRetry))
            {
                throw
            }
            Start-Sleep -Milliseconds 2000
        }
    }while($true)
}
function Invoke-InternalWebRequest($Uri, $Headers, $Method, $Body, $ContentType, [ref]$Result)
{
    $RetryCount = 0
    $MaxRetry = 3
    do
    {
        try
        {
            $ResultObject = Invoke-WebRequest -Uri $UpdateUrl -Headers $Header -Method 'PATCH' `
                -Body $InputJson  -ContentType "application/json" -UseBasicParsing
            ($Result.Value) += ($ResultObject)
            break
        }
        catch
        {
            Write-InformationTracing ("Retry Count: {0}, Exception: {1}." -f $RetryCount, $_.Exception)
            $RetryCount++
            if(!($RetryCount -le $MaxRetry))
            {
                throw
            }
            Start-Sleep -Milliseconds 2000
        }
    }while($true)
}
function Get-Header([ref]$Header, $AadAudience){
    try
    {
        $Header.Value['Content-Type'] = 'application\json'
        Write-InformationTracing ("The Authentication Type is system Assigned Identity based.")
        $endpoint = $env:IDENTITY_ENDPOINT
        $endpoint  
        $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 
        $Headers.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER) 
        $Headers.Add("Metadata", "True")   
        $authenticationResult = Invoke-RestMethod -Method Get -Headers $Headers -Uri ($endpoint +'?resource=' +$AadAudience)
        $accessToken = $authenticationResult.access_token
        $Header.Value['Authorization'] = "Bearer " + $accessToken
        $Header.Value["x-ms-client-request-id"] = $TaskId + "/" + (New-Guid).ToString() + "-" + (Get-Date).ToString("u")
    }
    catch
    {
        $ErrorMessage = ("Get-BearerToken: failed with [Exception: {0}]." -f $_.Exception)
        Write-Tracing -Level ErrorLevel -Message $ErrorMessage -DisplayMessageToUser
        Throw-TerminatingErrorMessage -Message $ErrorMessage
    }
}
function Get-ProtectionContainerToBeModified([ref] $ContainerMappingList)
{
    try
    {
        Write-InformationTracing ("Get protection container mappings : {0}." -f $VaultResourceId)
        $ContainerMappingListUrl = $ArmEndPoint + $VaultResourceId + "/replicationProtectionContainerMappings" + "?api-version=" + $AsrApiVersion
        Write-InformationTracing ("Getting the bearer token and the header.")
        Get-Header ([ref]$Header) $AadAudience
        $Result = @()
        Invoke-InternalRestMethod -Uri $ContainerMappingListUrl -Headers $header -Result ([ref]$Result)
        $ContainerMappings = $Result[0]
        Write-InformationTracing ("Total retrieved container mappings: {0}." -f $ContainerMappings.Value.Count)
        foreach($Mapping in $ContainerMappings.Value)
        {
            if(($Mapping.properties.providerSpecificDetails -eq $null) -or ($Mapping.properties.providerSpecificDetails.instanceType -ine "A2A"))
            {
                Write-InformationTracing ("Mapping properties: {0}." -f ($Mapping.properties))
                Write-InformationTracing ("Ignoring container mapping: {0} as the provider does not match." -f ($Mapping.Id))
                continue;
            }
            if($Mapping.Properties.State -ine "Paired")
            {
                Write-InformationTracing ("Ignoring container mapping: {0} as the state is not paired." -f ($Mapping.Id))
                continue;
            }
            Write-InformationTracing ("Provider specific details {0}." -f ($Mapping.properties.providerSpecificDetails))
            $MappingAutoUpdateStatus = $Mapping.properties.providerSpecificDetails.agentAutoUpdateStatus
            $MappingAutomationAccountArmId = $Mapping.properties.providerSpecificDetails.automationAccountArmId
            $MappingHealthErrorCount = $Mapping.properties.HealthErrorDetails.Count
            if($AutoUpdateAction -ieq "Enabled" -and
                ($MappingAutoUpdateStatus -ieq "Enabled") -and
                ($MappingAutomationAccountArmId -ieq $AutomationAccountArmId) -and
                ($MappingHealthErrorCount -eq 0))
            {
                Write-InformationTracing ("Provider specific details {0}." -f ($Mapping.properties))
                Write-InformationTracing ("Ignoring container mapping: {0} as the auto update is already enabled and is healthy." -f ($Mapping.Id))
                continue;
            }
            ($ContainerMappingList.Value).Add($Mapping.id)
        }
    }
    catch
    {
        $ErrorMessage = ("Get-ProtectionContainerToBeModified: failed with [Exception: {0}]." -f $_.Exception)
        Write-Tracing -Level ErrorLevel -Message $ErrorMessage -DisplayMessageToUser
        Throw-TerminatingErrorMessage -Message $ErrorMessage
    }
}
$OperationStartTime = Get-Date
$ContainerMappingList = New-Object System.Collections.Generic.List[System.String]
$JobsInProgressList = @()
$JobsCompletedSuccessList = @()
$JobsCompletedFailedList = @()
$JobsFailedToStart = 0
$JobsTimedOut = 0
$Header = @{}
$AzureRMProfile = Get-Module -ListAvailable -Name AzureRM.Profile | Select Name, Version, Path
$AzureRmProfileModulePath = Split-Path -Parent $AzureRMProfile.Path
Add-Type -Path (Join-Path $AzureRmProfileModulePath "Microsoft.IdentityModel.Clients.ActiveDirectory.dll")
$Inputs = ("Tracing inputs VaultResourceId: {0}, Timeout: {1}, AutoUpdateAction: {2}, AutomationAccountArmId: {3}." -f $VaultResourceId, $Timeout, $AutoUpdateAction, $AutomationAccountArmId)
Write-Tracing -Message $Inputs -Level Informational -DisplayMessageToUser
$CloudConfig = ("Tracing cloud configuration ArmEndPoint: {0}, AadAuthority: {1}, AadAudience: {2}." -f $ArmEndPoint, $AadAuthority, $AadAudience)
Write-Tracing -Message $CloudConfig -Level Informational -DisplayMessageToUser
ValidateInput
$SubscriptionId = Initialize-SubscriptionId
Get-ProtectionContainerToBeModified ([ref]$ContainerMappingList)
$Input = @{
  "properties"= @{
    "providerSpecificInput"= @{
        "instanceType" = "A2A"
        "agentAutoUpdateStatus" = $AutoUpdateAction
        "automationAccountArmId" = $AutomationAccountArmId
        "automationAccountAuthenticationType" = $AuthenticationType
    }
  }
}
$InputJson = $Input |  ConvertTo-Json
if ($ContainerMappingList.Count -eq 0)
{
    Write-Tracing -Level Succeeded -Message ("Exiting as there are no container mappings to be modified.") -DisplayMessageToUser
    exit
}
Write-InformationTracing ("Container mappings to be updated has been retrieved with count: {0}." -f $ContainerMappingList.Count)
try
{
    Write-InformationTracing ("Start the modify container mapping jobs.")
    ForEach($Mapping in $ContainerMappingList)
    {
    try {
            $UpdateUrl = $ArmEndPoint + $Mapping + "?api-version=" + $AsrApiVersion
            Get-Header ([ref]$Header) $AadAudience
            $Result = @()
            Invoke-InternalWebRequest -Uri $UpdateUrl -Headers $Header -Method 'PATCH' `
                -Body $InputJson  -ContentType "application/json" -Result ([ref]$Result)
            $Result = $Result[0]
            $JobAsyncUrl = $Result.Headers['Azure-AsyncOperation']
            Write-InformationTracing ("The modify container mapping job invoked with async url: {0}." -f $JobAsyncUrl)
            $JobsInProgressList += $JobAsyncUrl;
            # Rate controlling the set calls to maximum 60 calls per minute.
            # ASR throttling for set calls is 200 in 1 minute.
            Start-Sleep -Milliseconds 1000
        }
        catch{
            Write-InformationTracing ("The modify container mappings job creation failed for: {0}." -f $Ru)
            Write-InformationTracing $_
            $JobsFailedToStart++
        }
    }
    Write-InformationTracing ("Total modify container mappings has been initiated: {0}." -f $JobsInProgressList.Count)
}
catch
{
    $ErrorMessage = ("Modify container mapping jobs failed with [Exception: {0}]." -f $_.Exception)
    Write-Tracing -Level ErrorLevel -Message $ErrorMessage -DisplayMessageToUser
    Throw-TerminatingErrorMessage -Message $ErrorMessage
}
try
{
    while($JobsInProgressList.Count -ne 0)
    {
        Sleep -Seconds 30
        $JobsInProgressListInternal = @()
        ForEach($JobAsyncUrl in $JobsInProgressList)
        {
            try
            {
                Get-Header ([ref]$Header) $AadAudience
                $Result = Invoke-RestMethod -Uri $JobAsyncUrl -Headers $header
                $JobState = $Result.Status
                if($JobState -ieq "InProgress")
                {
                    $JobsInProgressListInternal += $JobAsyncUrl
                }
                elseif($JobState -ieq "Succeeded" -or `
                    $JobState -ieq "PartiallySucceeded" -or `
                    $JobState -ieq "CompletedWithInformation")
                {
                    Write-InformationTracing ("Jobs succeeded with state: {0}." -f $JobState)
                    $JobsCompletedSuccessList += $JobAsyncUrl
                }
                else
                {
                    Write-InformationTracing ("Jobs failed with state: {0}." -f $JobState)
                    $JobsCompletedFailedList += $JobAsyncUrl
                }
            }
            catch
            {
                Write-InformationTracing ("The get job failed with: {0}. Ignoring the exception and retrying the next job." -f $_.Exception)
                # The job on which the tracking failed, will be considered in progress and tried again later.
                $JobsInProgressListInternal += $JobAsyncUrl
            }
            # Rate controlling the get calls to maximum 120 calls each minute.
            # ASR throttling for get calls is 10000 in 60 minutes.
            Start-Sleep -Milliseconds 500
        }
        Write-InformationTracing ("Jobs remaining {0}." -f $JobsInProgressListInternal.Count)
        $CurrentTime = Get-Date
        if($CurrentTime -gt $OperationStartTime.AddMinutes($Timeout))
        {
            Write-InformationTracing ("Tracing modify cloud pairing jobs has timed out.")
            $JobsTimedOut = $JobsInProgressListInternal.Count
            $JobsInProgressListInternal = @()
        }
        $JobsInProgressList = $JobsInProgressListInternal
    }
}
catch
{
    $ErrorMessage = ("Tracking modify cloud pairing jobs failed with [Exception: {0}]." -f $_.Exception)
    Write-Tracing -Level ErrorLevel -Message $ErrorMessage  -DisplayMessageToUser
    Throw-TerminatingErrorMessage -Message $ErrorMessage
}
Write-InformationTracing ("Tracking modify cloud pairing jobs completed.")
Write-InformationTracing ("Modify cloud pairing jobs success: {0}." -f $JobsCompletedSuccessList.Count)
Write-InformationTracing ("Modify cloud pairing jobs failed: {0}." -f $JobsCompletedFailedList.Count)
Write-InformationTracing ("Modify cloud pairing jobs failed to start: {0}." -f $JobsFailedToStart)
Write-InformationTracing ("Modify cloud pairing jobs timedout: {0}." -f $JobsTimedOut)
if($JobsTimedOut -gt  0)
{
    $ErrorMessage = "One or more modify cloud pairing jobs has timedout."
    Write-Tracing -Level ErrorLevel -Message ($ErrorMessage)
    Throw-TerminatingErrorMessage -Message $ErrorMessage
}
elseif($JobsCompletedSuccessList.Count -ne $ContainerMappingList.Count)
{
    $ErrorMessage = "One or more modify cloud pairing jobs failed."
    Write-Tracing -Level ErrorLevel -Message ($ErrorMessage)
    Throw-TerminatingErrorMessage -Message $ErrorMessage
}
Write-Tracing -Level Succeeded -Message ("Modify cloud pairing completed.") -DisplayMessageToUser

Ruční správa aktualizací

  1. Pokud jsou na virtuálních počítačích nainstalované nové aktualizace Mobility, zobrazí se následující oznámení: K dispozici je nová aktualizace agenta replikace Site Recovery. Kliknutím ho nainstalujte.

    Replicated items window

  2. Výběrem oznámení otevřete stránku výběru virtuálního počítače.

  3. Zvolte virtuální počítače, které chcete upgradovat, a pak vyberte OK. Aktualizace Mobility se spustí pro každý vybraný virtuální počítač.

    Replicated items VM list

Známé problémy a řešení potíží

Pokud dojde k problému s automatickými aktualizacemi, v části Problémy s konfigurací na řídicím panelu trezoru se zobrazí oznámení o chybě.

Pokud nemůžete povolit automatické aktualizace, projděte si následující běžné chyby a doporučené akce:

  • Chyba: Nemáte oprávnění k vytvoření účtu Azure Spustit jako (instanční objekt) a udělení role Přispěvatel instančnímu objektu.

    Doporučená akce: Ujistěte se, že je přihlášený účet přiřazený jako Přispěvatel, a zkuste to znovu. Další informace o přiřazování oprávnění najdete v části Postupy: Použití portálu k vytvoření aplikace Microsoft Entra a instančního objektu, který má přístup k prostředkům.

    Pokud chcete většinu problémů vyřešit po povolení automatických aktualizací, vyberte Opravit. Pokud není tlačítko opravy dostupné, podívejte se na chybovou zprávu zobrazenou v podokně nastavení aktualizace rozšíření.

    Site Recovery service repair button in extension update settings

  • Chyba: Účet Spustit jako nemá oprávnění pro přístup k prostředku služby Recovery Services.

    Doporučená akce: Odstraňte a znovu vytvořte účet Spustit jako. Nebo se ujistěte, že aplikace Microsoft Entra účtu Automation Spustit jako má přístup k prostředku služby Recovery Services.

  • Chyba: Účet Spustit jako nebyl nalezen. Některá z těchto možností byla odstraněna nebo nebyla vytvořena – aplikace Microsoft Entra, instanční objekt, role, prostředek certifikátu automation, prostředek Připojení ion služby Automation nebo kryptografický otisk není stejný mezi certifikátem a Připojení ionem.

    Doporučená akce: Odstraňte a znovu vytvořte účet Spustit jako.

  • Chyba: Platnost certifikátu Azure Spustit jako používaného účtem Automation brzy vyprší.

    Certifikát podepsaný svým držitelem vytvořený pro účet Spustit jako vyprší jeden rok od data vytvoření. Před vypršením platnosti ho můžete kdykoli obnovit. Pokud jste se zaregistrovali k e-mailovým oznámením, obdržíte e-maily také v případě, že se vyžaduje akce na vaší straně. Tato chyba se zobrazí dva měsíce před datem vypršení platnosti a v případě vypršení platnosti certifikátu se změní na kritickou chybu. Po vypršení platnosti certifikátu nebude automatická aktualizace funkční, dokud neobnovíte stejnou platnost.

    Doporučená akce: Chcete-li tento problém vyřešit, vyberte opravit a potom obnovit certifikát.

    renew-cert

    Poznámka:

    Po obnovení certifikátu aktualizujte stránku, aby se zobrazil aktuální stav.

Další kroky

Přečtěte si další informace o tom, jak migrovat typ ověřování účtů Automation do spravovaných identit.