Gérer ses VM Azure...

Ceci est un Notepad en évolution... Revenez...

Start / Stop

Un des intérêts d'une VM dans le cloud, c’est de n'en payer que l'usage. Pour la faire courte et pour donner suite à mon article sur les Bureaux à distance, je me suis retrouvé pour ce client avec VM qui n'était pas assez puissante. Sous Azure, c’est facile, en deux clics on change la taille de la VM, on choisit une instance avec plus de CPU, RAM, IO et bien sur le tarif augmente... Et si Azure est fonctionnellement remarquable, les tarifs sont purement stratosphériques si on les compare à une VM sur un serveur dédié chez Scaleway ou OVH (qu'il faudra bien sur géré, ce qui a aussi un coût et n’est pas faisable facilement par tout le monde). Bien sur ont peut facilement imaginer que les grand comptes obtiennent des remises tout aussi conséquents, sauf que pour une PME ça reste bien souvent inaccessible.

Vu que cette VM ne va être utilisée qu'aux heures de bureau, une façon de faire quelques économies va consister à programmer une heure de démarrage et une heure d'arrêt. Pour l’arrêt c’est facile, il suffit de configurer ça dans les paramètres de la VM. Mais allez donc savoir pourquoi il n'y a pas la même facilité pour la démarrer ? Chez Microsoft il appellent ça By Design. Il doit bien y avoir une raison puisque qu'il semblerait que ce soit à peu près le même fonctionnement chez les autres fournisseurs d'instances cloud et même chez OVH ou Scaleway. Bref, ce serait trop facile.

Il y plusieurs solution pour palier à ça !

Azure CLI

On peu bricoler quelque chose avec Azure CLI (Linux, Mac, Windows) et ainsi lancer un script qui va lancer ou stopper a VM. Pas très sexy ni trop sécurisé. 

Attention : Il ne faut pas se contenter d’arrêter la VM depuis l'O/S car si cela arrêtera bien la VM, elle continuera à être facturée, simple, mais sans intérêt. Pour qu'elle ne soit plus facturé il faut la désallouer (Deallocate) et ainsi seul le stockage continuera à être facturée.

Démarrer la VM

az vm start --name MyVM --resource-group MyVMGroup

Arrêter et désallouer

az vm stop --name MyVM --resource-group MyVMGroup
az vm deallocate --name MyVM --resource-group MyVMGroup

Il est également possible de faire ça en utilisant directement les ID des VM

az start --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"
az stop --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"
az deallocate --ids "/subscriptions/a35d316a-2a2a-48e2-8834-55481f7bbb48/resourceGroups/WIN16VM/providers/Microsoft.Compute/virtualMachines/Win16VM"

Azure Automation

Tout ça n'est pas très sexy ni user friendly. Une autre façon de faire va consister pour l'administrateur de programmer le démarrage de la VM et son arrêt en considérant que l'utilisateur s'en servira à heures fixes. Pour l’arrêt on va se servir de la fonction intégrée car elle est capable de base d'envoyer un mail à l’utilisateur lui proposant d'outrepasser l’arrêt programmé sans avoir à se connecter. Pratique.

Pour le démarrage par contre ça va être un peu plus compliqué. On va commencer par créer un compte Automation, simplement en cherchant dans la barre de recherche. Comme tout sur Azure, ce service est également payant, mais dans sa grande bonté Microsoft nous offre (jusqu'à quand ?) un petit forfait gratuit tous les mois qui sera amplement suffisant pour ce qu'on va faire.

Une fois dans notre compte Automation on va créer un Runbook, on lui donne un nom et on choisit un Flux de travail PowerShell (PowerShell Workflow) et on le crée. Une fois créé on va aller l'éditer

workflow Start-Ma-VM
{
# Association to the Azure subscribtion
$Conn = Get-AutomationConnection -Name AzureRunAsConnection
Add-AzureRMAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint

# Start the virtual machine
Start-AzureRMVM -ResourceGroupName "MonGroupeDeRessources" -Name "MaVM"
}

Il ne reste plus qu'à aller dans le menu Planifications et d'en ajouter une en configurant l'heure de démarrage et la récurrence, sans oublier de choisir le bon fuseau horaire. Si on veut envoyer une notification par un mail ou faire autre chose il est bien sur possible de le faire dans le script.

A ce stade ça reste toutefois limité, la VM va démarrer tous les jours, même quand personne ne travaille.

Heureusement il est également possible de créer un WebHook et ainsi démarrer la VM depuis une URL. Ici depuis PowerShell mais on peut tout à fait imaginer intégrer ça dans un portail intranet ou un simple raccourcis sur le poste de l'utilisateur... Ou mieux utiliser un planificateur évolué tenant compte des jours fériés et des week-ends, voire interagir avec un agenda Google ou Microsoft 365...

PS C:\Users\Lionel> Invoke-WebRequest -Uri https://6bc1fsdfs-aesdf4c2f-8d31dsfsdf7fsd5f.webhook.we.azure-automation.net/webhooks?token=ZHuMYFqgqgfqsqgdgB%2qsdgfqsdgjc%3d -Method POST

Alternatives

Si la lecture de la documentation n'est pas toujours très lisibles, il faut bien admettre qu'Azure est très complet. Et si cela ne vous suffit pas il existe des fournisseurs de service qui se sont spécialisés dans la planification des VM et plus globalement vous aideront à réduire les coûts du cloud, que ce soit sur Azure, AWS ou GoogleCloud. Je pense par exemple à ParkMyCloud ou MyCloudToolbox, mais il y en d'autres. Se posera alors la question de la confiance que vous accorderez à ces services.

L'accès des clients aux VM

Comme on l'a vu le plus simple consiste à utiliser le client d'accès à distance. Le protocole RDP n'étant pas franchement des plus sur. Sécuriser RDP imposerait de passer par des certificats et une passerelle complexe et coûteuse ou par une alternative non Microsoft du genre Royal TS Server. La logique impose de passer par un VPN. Azure propose bien entendu tout une panoplie de solutions de sécurité, n'ayant ni envie de passer à la caisse ni celle de me compliquer la vie pour ce type d'utilisation, je vais simplement utiliser ZeroTier. C'est plus un SDN qu'un VPN, c'est OpenSource et gratuit et ça fait parfaitement le travail (j'en avais déjà parlé et on trouve maintenant une multitude d'articles).

On donnera une IP fixe au serveur et tous les clients autorisés pourront accéder au serveur RDP. Ensuite on prendra soin de ne plus autoriser l'accès via l'IP externe Azure ou de forcer un ACL sur une IP cliente sure. Easy, d'autant plus que mon client utilise déja cette solution pour sécuriser l'accès à des ressources SMB distantes.

Scénario pratique....

Sur le poste de on prépare un script PowerShell qui va lancer le WebHook pour démarrer la VM Azure, attendre que le serveur soit lancé, tester le port RDP et lancer le Bureau à distance, et au passage notifier un IT si quelque chose se passe mal. S'il doit y revenir dans la journée il ne lancera que le client RDP (Bureau à distance), en fin de journée le serveur sera programmé pour s’arrêter tout seul et si l’utilisateur veut faire des heures supplémentaires il n'aura qu'à cliquer dans le mail reçu pour reprogrammer l’arrêt...

On commence par brouiller à minima le mot de passe du mail...

"P4ssww0rd!" | Convertto-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-file C:\Users\User1\admin.txt

Et ensuite on prépare un petit script qui se lancera depuis un raccourcis sur le bureau...

$password = Get-Content C:\Users\User1\admin.txt | Convertto-SecureString # On récupère le mot de passe encrypté dans un fichier...
$username = "[email protected]"
$From = "[email protected]"
$To = "user@prestataire"
$SMTPServer = "mail.gandi.net"
$SMTPPort = 587
$subject = "Client : Utilisateur (Machine)"
$Credential = New-Object System.Management.Automation.PSCredential ($username, $password)

try {
    $response = Invoke-WebRequest -Uri https://6bcghgdfh-afgh-4ghgfdf-8ghfgdh5f.webhook.we.azure-automation.net/webhooks?token=ZHuMYFshghshhh5+fgshs+f5h5+65sh -Method POST
    if($response.StatusCode -eq 202) {
        Write-Host "waiting..."
        sleep 60   # Temps d’attente estimé pour le démarrage de la VM
        if (Test-NetConnection 10.17.22.15 -Port 3389 | ? { $_.TcpTestSucceeded } ) {
            C:\Users\User1\vm.rdp  # On lance le client RDP
        } else {
            cls
	    Write-Output "RDP 1" # Si test NetConnection échoue (pas de réponse)
            Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur NetConnection Fail' -Credential $Credential  -Port $SMTPPort -UseSsl
	    sleep 15
        }
    } else {
        cls
	Write-Output "Erreur RDP (l'administrateur a recu un mail d'avertissement et va intervenir !)" # Si NetConnection code autre que 202
        Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur NetConnection 40x ou 500x' -Credential $Credential  -Port $SMTPPort -UseSsl
	sleep 15
    }
} catch {
    cls
    Write-Output "Erreur VM (l'administrateur a recu un mail d'avertissement et va intervenir !)" # Erreur NetConnection code 40x ou 50x
    Send-MailMessage -To $To -From $From -Subject $subject -SmtpServer $SMTPServer -Body 'Erreur au lancement de la VM' -Credential $Credential  -Port $SMTPPort -UseSsl
    sleep 15
}

Ainsi, contrairement à une planification en début de journée, la VM ne sera lancée que quand l'utilisateur en a l'usage afin d'économiser sur la facture Azure. Au besoin on peut également faire un petit script pour l'éteindre.

Migration

Dans un autre contexte je dois migrer une VM Azure vers ESXi. Expérience à venir.

Sources