Share via


Automatische update van de Mobility-service in Azure-naar-Azure-replicatie

Azure Site Recovery maakt gebruik van een maandelijkse releasefrequentie om eventuele problemen op te lossen en bestaande functies te verbeteren of nieuwe functies toe te voegen. Als u actueel wilt blijven met de service, moet u elke maand een patchimplementatie plannen. Om de overhead voor elke upgrade te voorkomen, kunt u Site Recovery toestaan om onderdeelupdates te beheren.

Zoals vermeld in de architectuur voor herstel na noodgevallen van Azure naar Azure, wordt de Mobility-service geïnstalleerd op alle virtuele Azure-machines (VM's) waarvoor replicatie is ingeschakeld van de ene Azure-regio naar de andere. Wanneer u automatische updates gebruikt, werkt elke nieuwe release de Mobility-service-extensie bij.

Notitie

Het wordt aanbevolen de Azure Az PowerShell-module te gebruiken om te communiceren met Azure. Zie Azure PowerShell installeren om aan de slag te gaan. Raadpleeg Azure PowerShell migreren van AzureRM naar Az om te leren hoe u naar de Azure PowerShell-module migreert.

Hoe automatische updates werken

Wanneer u Site Recovery gebruikt om updates te beheren, wordt er een globaal runbook (gebruikt door Azure-services) geïmplementeerd via een Automation-account dat is gemaakt in hetzelfde abonnement als de kluis. Elke kluis maakt gebruik van één Automation-account. Voor elke VIRTUELE machine in een kluis controleert het runbook op actieve automatische updates. Als er een nieuwere versie van de Mobility-service-extensie beschikbaar is, wordt de update geïnstalleerd.

Het standaardrunbookschema vindt dagelijks om 12:00 uur plaats in de tijdzone van de geografie van de gerepliceerde VM. U kunt het runbookschema ook wijzigen via het Automation-account.

Notitie

Vanaf updatepakket 35 kunt u een bestaand Automation-account kiezen dat moet worden gebruikt voor updates. Vóór updatepakket 35 heeft Site Recovery standaard het automation-account gemaakt. U kunt deze optie alleen selecteren wanneer u replicatie voor een VIRTUELE machine inschakelt. Deze is niet beschikbaar voor een virtuele machine waarvoor replicatie al is ingeschakeld. De instelling die u selecteert, is van toepassing op alle virtuele Azure-machines die in dezelfde kluis zijn beveiligd.

Voor het inschakelen van automatische updates is geen herstart van uw Azure-VM's vereist of is dit van invloed op de lopende replicatie.

Taakfacturering in het Automation-account is gebaseerd op het aantal taakruntimeminuten dat in een maand wordt gebruikt. Taakuitvoering duurt een paar seconden tot ongeveer een minuut per dag en wordt behandeld als gratis eenheden. Standaard zijn 500 minuten opgenomen als gratis eenheden voor een Automation-account, zoals wordt weergegeven in de volgende tabel:

Gratis eenheden inbegrepen (elke maand) Prijs
Taakruntime 500 minuten ₹0,14/minuut

Automatische updates inschakelen

Er zijn verschillende manieren waarop Site Recovery de extensie-updates kan beheren:

Beheren als onderdeel van de stap replicatie inschakelen

Wanneer u replicatie inschakelt voor een virtuele machine die begint vanuit de VM-weergave of vanuit de Recovery Services-kluis, kunt u Toestaan dat Site Recovery updates voor de Site Recovery-extensie beheert of deze handmatig beheert.

Extension settings

De instellingen voor het bijwerken van extensies in de kluis in- of uitschakelen

  1. Ga vanuit de Recovery Services-kluis naar Site Recovery-infrastructuur beheren>.

  2. Selecteer Aan onder Voor Update van azure Virtual Machines-extensie>Instellingen> Site Recovery wilt beheren.

    Als u de extensie handmatig wilt beheren, selecteert u Uit.

    Belangrijk

    Wanneer u Site Recovery toestaan kiest om te beheren, wordt de instelling toegepast op alle VM's in de kluis.

  3. Selecteer Opslaan.

Extension update settings

Notitie

Met beide opties wordt u op de hoogte gehouden van het Automation-account dat wordt gebruikt voor het beheren van updates. Als u deze functie voor het eerst in een kluis gebruikt, wordt standaard een nieuw Automation-account gemaakt. U kunt de instelling ook aanpassen en een bestaand Automation-account kiezen. Zodra dit is gedefinieerd, gebruiken alle volgende acties om replicatie in dezelfde kluis in te schakelen het geselecteerde Automation-account. Op dit moment worden in de vervolgkeuzelijst alleen automation-accounts weergegeven die zich in dezelfde resourcegroep bevinden als de kluis.

Gebruik het volgende script voor een aangepast Automation-account:

Belangrijk

Voer het volgende script uit in de context van een Automation-account. Dit script maakt gebruik van door het systeem toegewezen beheerde identiteiten als verificatietype.

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

Updates handmatig beheren

  1. Als er nieuwe updates zijn voor de Mobility-service geïnstalleerd op uw VM's, ziet u de volgende melding: Er is een update van de nieuwe Site Recovery-replicatieagent beschikbaar. Klik om te installeren.

    Replicated items window

  2. Selecteer de melding om de vm-selectiepagina te openen.

  3. Kies de VM's die u wilt upgraden en selecteer VERVOLGENS OK. De Mobility-service bijwerken wordt gestart voor elke geselecteerde VM.

    Replicated items VM list

Veelvoorkomende problemen en probleemoplossing

Als er een probleem is met de automatische updates, ziet u een foutmelding onder Configuratieproblemen in het kluisdashboard.

Als u automatische updates niet kunt inschakelen, raadpleegt u de volgende veelvoorkomende fouten en aanbevolen acties:

  • Fout: U bent niet gemachtigd om een Azure Uitvoeren als-account (service-principal) te maken en de rol Inzender aan de service-principal te verlenen.

    Aanbevolen actie: zorg ervoor dat het aangemelde account is toegewezen als Inzender en probeer het opnieuw. Zie de sectie Vereiste machtigingen van Procedures voor meer informatie over het toewijzen van machtigingen: Gebruik de portal om een Microsoft Entra-toepassing en service-principal te maken die toegang heeft tot resources.

    Als u de meeste problemen wilt oplossen nadat u automatische updates hebt ingeschakeld, selecteert u Herstellen. Als de herstelknop niet beschikbaar is, raadpleegt u het foutbericht dat wordt weergegeven in het deelvenster instellingen voor extensie-updates.

    Site Recovery service repair button in extension update settings

  • Fout: Het Uitvoeren als-account heeft niet de machtiging om toegang te krijgen tot de Recovery Services-resource.

    Aanbevolen actie: Verwijder en maak vervolgens het Uitvoeren als-account opnieuw. Of zorg ervoor dat de Microsoft Entra-toepassing van het Automation Uitvoeren als-account toegang heeft tot de Recovery Services-resource.

  • Fout: Uitvoeren als-account is niet gevonden. Een van deze is verwijderd of niet gemaakt: Microsoft Entra Application, Service Principal, Role, Automation Certificate Asset, Automation Verbinding maken ion asset of de vingerafdruk is niet identiek tussen Certificaat en Verbinding maken ion.

    Aanbevolen actie: Verwijder en maak vervolgens het Uitvoeren als-account opnieuw.

  • Fout: het Uitvoeren als certificaat van Azure dat door het Automation-account wordt gebruikt, verloopt bijna.

    Het zelfondertekende certificaat dat is gemaakt voor het Uitvoeren als-account verloopt één jaar vanaf de aanmaakdatum. U kunt het certificaat op elk gewenst moment vernieuwen voordat het verloopt. Als u zich hebt geregistreerd voor e-mailmeldingen, ontvangt u ook e-mailberichten wanneer een actie van uw kant is vereist. Deze fout wordt twee maanden vóór de vervaldatum weergegeven en wordt gewijzigd in een kritieke fout als het certificaat is verlopen. Zodra het certificaat is verlopen, werkt automatisch bijwerken pas als u hetzelfde verlengt.

    Aanbevolen actie: Als u dit probleem wilt oplossen, selecteert u Herstellen en vervolgens Certificaat vernieuwen.

    renew-cert

    Notitie

    Nadat u het certificaat hebt vernieuwd, vernieuwt u de pagina om de huidige status weer te geven.

Volgende stappen

Meer informatie over het migreren van het verificatietype van de Automation-accounts naar beheerde identiteiten.