Loupe

Automatiser les backups SQL Azure avec Azure Function et Azure Powershell

Azure SQL Database étant un service PaaS, Microsoft met à notre disposition un système de backup automatique. Ces backups sont utilisables depuis le portail Azure et avec les outils en ligne de commande Azure pour restaurer facilement des bases de données sur des serveurs Sql Azure.

En plus de ce système de backup automatique, il peut être utile d’effectuer des backups chaque jour à des heures précises, pour cela nous allons héberger un script Azure PowerShell personnalisé dans une Fonction Azure ! 

 

Backup automatique, avantages et inconvénients

Trois types de sauvegardes sont automatiquement créés par Azure : complètes, différentielles ainsi que des sauvegardes des logs de transactions. La fréquence de sauvegarde et le temps de rétention dépend du niveau de service choisi (« pricing tier »).

Les logs de transaction sont sauvegardés toutes les 5/10 minutes, les sauvegardes différentielles toutes les heures et les sauvegarde complètes sont effectuées chaque semaine. Le niveau de service Basic permet de conserver les sauvegardes pendant 7 jours alors que les niveaux Standard et Premium étendent le temps de rétention jusqu’à 35 jours.

Ce système automatique est certes pratique, mais il nous est impossible de le personnaliser :

  • Impossible de définir explicitement le nombre de sauvegardes et leur fréquence
  • Impossible d’accéder directement au compte de stockage dans lequel les sauvegardes sont stockées au format .bacpac

Nous allons donc outrepasser ces deux limitations en combinant l’utilisation d’Azure PowerShell pour générer des sauvegardes (.bacpac) dans un compte de stockage et Azure Function pour « scheduler » les sauvegardes.

 

Générer des backups dans un compte de stockage personnalisé avec Azure Powershell

Avec Azure Powershell, il est relativement simple de créer une sauvegarde complète d’une base de données SQL Azure : La commande New-AzureRmSqlDatabaseExport permet de générer un fichier .bacpac et de le stocker dans un compte de stockage Azure.

Sa syntaxe est la suivante :

New-AzureRmSqlDatabaseExport [-DatabaseName] <String> [-ServerName] <String> -StorageKeyType <StorageKeyType> -StorageKey <String> -StorageUri <Uri> -AdministratorLogin <String> -AdministratorLoginPassword <SecureString> [-AuthenticationType <AuthenticationType>] [-ResourceGroupName] <String>

 

Pour utiliser cette commande avec succès, il est nécessaire de spécifier les informations suivantes :

  • Informations de connexion du server SQL (AdministratorLogin, AdministratorPassword)
  • Nom de la base de données cible (DatabaseName)
  • Informations du compte de stockage dans lequel nous voulons stocker le fichier .bacpac généré (storageUri, storageKey, storageKeyType)
  • Le nom du groupe de ressources dans lequel se trouve le serveur Sql Azure (ResourceGroupName)

 

Voici le script de sauvegarde :

$subcriptionId = "********"
$resourceGroupName = "project-tra-rg"

#Sql server and target database
$ServerName = "tra-sql-srv"
$DatabaseName = "tra-db"

#target storage informations
$StorageKeytype = "StorageAccessKey"
$StorageKey = "********"
$storageUriTarget = "https://trastorageac.blob.core.windows.net/backup"

#sql credentials
$pwdClear = "*******"
$userName = "admin-tra"
$pwd = ConvertTo-SecureString $pwdClear -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ($userName, $pwd)

#Backup naming logic
$bacpactName = "{0:yyyy-MM-dd}.bacpac" -f (get-date)
$uriTarget = $storageUriTarget + '/' + $DatabaseName + '-' + $bacpactName

Login-AzureRmAccount -SubscriptionId $subcriptionId

$exportRequest = New-AzureRmSqlDatabaseExport -ResourceGroupName $ResourceGroupName -ServerName $ServerName -DatabaseName $DatabaseName -StorageKeytype $StorageKeytype -StorageKey $StorageKey -StorageUri $uriTarget -AdministratorLogin $creds.UserName -AdministratorLoginPassword $creds.Password

while ($exportRequest.Status -eq "InProgress")
{
   $exportRequest = Get-AzureRmSqlDatabaseImportExportStatus -OperationStatusLink $exportRequest.OperationStatusLink
   [Console]::Write(" Export in progress..")
   Start-Sleep -s 10
}

if ($exportRequest.Status -eq "Succeeded")
{
   [Console]::Write(" Export done")
}

 

Ce script est facilement utilisable depuis toute machine ayant préalablement installé les modules Azure PowerShell AzureRM.*. La commande Login-AzureRmAccount (1) avec le paramètre -SubscriptionId ouvre une fenêtre d’authentification qui permet à l’utilisateur de rentrer ses informations d’authentification.

Notre objectif final, est que ce script fonctionne dans une Fonction Azure, la fonction se veut autonome et ne peut donc pas demander à un utilisateur de s’authentifier via un prompt ! La solution consiste donc à modifier le mode d’authentification à Azure pour qu’il ne soit pas manuel mais automatique en utilisant une application Azure Active Directory (AAD) avec un « service principal » lié :

 

Login-AzureRmAccount -ServicePrincipal -TenantId $tenantId -Credential $mycreds

 

La syntaxe ci-dessus (2) utilise l’identifiant de tenant de l’abonnement Azure ainsi que les informations de connexion vers une application ADD, soit le couple identifiant du service principal / mot de passe.

Le script suivant crée automatiquement une application Azure AD et son service principal lié. Il définit trois outputs (tenantId, Azure AD App Password and ServicePrincipalId) utilisables avec la nouvelle syntaxe (2) de la commande Login-AzureRmAccount.

 

$role = "owner"

# Azure AD app configuration
$uniqueIdentifier = [guid]::NewGuid()
$appDisplayName = [String]::Format("app.{0}.{1}", $env:USERNAME, [guid]::NewGuid())
$appHomePage = [String]::Format("http://{0}", $appDisplayName)
$AppUris = $appHomePage
$password = "addProxyApp"
$subsciptionId = "*********"

Login-AzureRmAccount -SubscriptionId $subsciptionId

$tenantId = (Get-AzureRmSubscription -SubscriptionId $subId).TenantId

#Create the Azure Ad app
$azureAdApp = New-AzureRmADApplication -DisplayName $appDisplayName -HomePage $appHomePage -IdentifierUris $AppUris -Password $password
$azureAdAppId = $azureAdApp.ApplicationId

#Define a service principal on the app
$sp = New-AzureRmADServicePrincipal -ApplicationId $azureAdAppId

#Assign a role to the service principal
New-AzureRmRoleAssignment -RoleDefinitionName $role -ServicePrincipalName $sp.ServicePrincipalNames[0]

Write-Output "Service principal Id: $azureAdAppId"
Write-Output "Azure tenant Id: $tenantId"
Write-Output "App password : $password"

 

Une fois le script précédent exécuté, nous pouvons mettre à jour la phase d’authentification du script de sauvegarde en remplace la ligne phase d’authentification (1) avec le code suivant :

$password = "******"
$tenantId = "*******"
$servicePrincipalId = "******"
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($servicePrincipalId, $secpasswd)

Login-AzureRmAccount -ServicePrincipal -TenantId $tenantId -Credential $mycreds

 

Le script final de sauvegarde est disponible ici et le script de création de l'application Azure AD ici :) 

 

Planification des sauvegardes avec une fonction Azure de type « Time Trigger » 

La base de données que nous souhaitons sauvegarder se nomme « tra-db », elle est placée sur un serveur Azure SQL nommé « tra-sql-srv ». Un compte de stockage cible « storageac » a pour objectif de stocker les sauvegardes. Au sein de ce compte de stockage, un conteneur nommé « backup » a été créé au préalable : 

1.png

Dans la partie précédente nous avons implémenté un script Azure Powershell avec un mode d’authentification autonome. Il nous faut maintenant héberger ce script dans un Fonction Azure dont voici les caractéristiques :

  • Type : TimeTrigger (nous permettra de planifier l’exécution de la fonction avec une expression CRON)
  • Langage : PowerShell
  • Type de plan de service : Dynamique (nous voulons exécuter un backup tous les jours à 23h, avec ce type de plan de service nous paierons uniquement à l’exécution de la fonction)

2.png

Nous allons créer une application Azure Function nommée « tra-function » dont le groupe de ressources existant « project-tra-rg ». La fonction quant à elle est nommée « backup_tra-db », nous définissons sa récurrence d’exécution avec l’expression CRON suivante : 0 0 23 * * * 

3.png

Au sein du script de sauvegarde, de nombreux paramètres sont nécessaires. Une fonction Azure s’exécute dans le contexte d’une application web Azure, il est donc possible de stocker les valeurs des paramètres nécessaires au script dans les paramètres (« application settings ») de l’application web Azure sous-jacente. Les paramètres de l’application de web sont ensuite accessibles depuis les fonctions par l’intermédiaire des variables d’environnements : 

6.png

Une fois la fonction créée, et les paramètres stockés au niveau de l’application web, il suffit d’ajouter le script de sauvegarde tel quel dans la fonction Azure et de faire correspondre les paramètres du script avec les variables d’environnement correspondantes. Le processus de backup automatique est prêt à être utilisé : 

5.png

Les modules AzureRM.* Powershell sont installés par défaut au niveau de la fonction Azure, nous n’avons donc pas besoin de les installer explicitement depuis KUDU.

 

Plan de consommation dynamique, augmentation du "Timeout"

Comme expliqué précédemment, nous utilisons un plan de service dynamique pour exécuter la fonction. Par défaut, ce type de plan de service permet à la fonction de s’exécuter au maximum pendant 5 minutes. Selon la taille de la base de données à sauvegarder, 5 minutes peuvent se révéler trop courtes pour que la sauvegarde soit réalisée avec succès :

7.png

La solution consiste à augmenter la limite de Timeout de la fonction en éditant le fichier host.json depuis KUDU. Ma fonction se nomme « tra-function », nous pouvons donc accéder à ce fichier en utilisant le chemin suivant : https://tra-function.scm.azurewebsites.net/dev/wwwroot/host.json. Il suffit de mettre à jour le nœud json functionTimout pour étendre la limite de Timout à 10 minutes :

"functionTimeout": "00:10:00"

Happy coding :) 

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus