Remote PowerShell vers une VM dans Windows Azure

Nous allons cette fois nous intéresser au remote management (WinRM), dorénavant ouvert par défaut sur les machines virtuelles Windows que l’on crée dans Windows Azure. Par défaut donc, nous avons, en plus de RDP, un second endpoint nommé WinRmHTTPs :

Endpoints par défaut sur une VM Windows

Un scénario où cela prend tout son sens consiste, juste après la création de la VM, à lancer un script de configuration complet. Cela peut inclure l’installation de rôles et fonctionnalités, d’applications ou de sites web.

Remarquez qu’il s’agit d’une connexion https. Cela est en effet nécessaire pour l’authentification mutuelle lors de la connexion, lorsque les deux systèmes ne sont pas dans le même domaine. Cela dit, cela suppose que le client fasse confiance au certificat SSL de la VM. Si l’on est pressé et pas très soucieux de la sécurité, cela peut donner ceci, pour une connexion interactive directe sans vérification du certificat :

 $vmname = "my_vm"
$svcname = "my_svc"
$username = "my_user"
$uri = Get-AzureWinRMUri -ServiceName $svcname -Name $vmname
$option = New-PSSessionOption -SkipCACheck
Enter-PSSession -ConnectionUri $uri -Credential "$vmname\$username" `
    -SessionOption $option

Dans cet exemple, on utilise Get-AzureWinRMUri, qui “Retrieves the uri to WinRM https listener to a VM or a list of VMs in a hosted service”, selon sa doc. Cet URI nous permet de récupérer le nom DNS et le port. Ensuite, si l’on oublie la sécurité, on utilise l’option –SkipCACheck pour la session PowerShell distante.

Démonstration : Lors de l’exécution de la commande Enter-PSSession avec l’option -Credential, un popup nous demande le mot de passe du compte indiqué.

connexion interactive sans vérification du certificat

C’est un bon début, juste pour se convaincre que la connexion fonctionne. Maintenant si l’on veut exécuter du script sur la VM, il ne faut plus lancer un shell interactive avec Enter-PSSession, mais plutôt exécuter un ScriptBlock avec la commande Invoke-Command. Par exemple, pour installer IIS :

 $vmname = "my_vm"
$svcname = "my_svc"
$username = "my_user"
$cred = Get-Credential -UserName "$vmname\$username" `
    -Message "Enter credential for $vmname"
$uri = Get-AzureWinRMUri -ServiceName $svcname -Name $vmname
$option = New-PSSessionOption -SkipCACheck
Invoke-Command -ConnectionUri $uri -Credential $cred `
    -SessionOption $option -ScriptBlock {
        Import-Module -Name ServerManager
        Install-WindowsFeature -Name Web-Server -IncludeManagementTools
    }

Au passage, j’ai sorti la demande de mot de passe avant le Invoke-Command, grâce à Get-Credential. Cela permettrait par exemple d’exécuter plusieurs Invoke-Command sans se réauthentifier. Démonstration :

Exécution d'un script sans vérification du certificat

Maintenant, s’il faut exécuter ce script de configuration dès le provisionnement de la machine virtuelle, il suffit d’utiliser l’option -WaitForBoot de New-AzureVM, de façon à déclencher le InvokeCommand dès que la VM est prête.

Pour finir, comment récupérer le certificat du endpoint WinRmHTTPs de la nouvelle VM ? C’est encore assez délicat mais faisable : il faut tout d’abord récupérer le thumbrint du certificat (DefaultWinRmCertificateThumbprint), puis le certificat lui-même. Ensuite, il faut importer le certificat dans le store des autorités de certification racines de confiance… Je m’inspire ici directement de l’article suivant, qui détaille tout le procédé :

https://michaelwasham.com/2013/04/16/windows-azure-powershell-updates-for-iaas-ga/

Mais j’utilise une autre méthode plus simple à mon goût (certutil.exe) pour faire l’import du certificat.

 $vmname = "my_vm"
$svcname = "my_svc"
$vm = Get-AzureVM -ServiceName $svcname -Name $vmname
$certthumbrint = $vm.VM.DefaultWinRmCertificateThumbprint
$cert = Get-AzureCertificate -ServiceName $svcname `
    -Thumbprint $certthumbrint -ThumbprintAlgorithm sha1
$cert.Data | Out-File -FilePath ".\my_cert.cer"
certutil.exe -f -addstore Root .\my_cert.cer

$username = "my_user"
$cred = Get-Credential -UserName "$vmname\$username" `
    -Message "Enter credential for $vmname"

$uri = Get-AzureWinRMUri -ServiceName $svcname -Name $vmname

Invoke-Command -ConnectionUri $uri -Credential $cred -ScriptBlock {
    Import-Module -Name ServerManager
    Install-WindowsFeature -Name Web-Server -IncludeManagementTools
}

Le résultat est que l’on peut _enfin_ se passer de l’option -SkipCACheck qui, avouons-le, n’était franchement pas du meilleur effet.

Exécution d'un script à distance avec validation du certificat

Enfin, voici un script complet avec la création d’une VM avec un listener sur le port 80, puis l’installation d’IIS :

 $vmname = "ma_vm"
$svcname = $vmname
$user = "mon_user"
$pwd = "m0n_mo7_d3_passe"

$image = "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-Datacenter-201306.01-en.us-127GB.vhd"
New-AzureVMConfig -Name $vmname -InstanceSize Small -ImageName $image |
  Add-AzureProvisioningConfig -Windows -AdminUsername $user -Password $pwd |
  Add-AzureEndpoint -Name "http" -Protocol tcp -LocalPort 80 -PublicPort 80 |
  New-AzureVM -ServiceName $vmname -Location "North Europe" -WaitForBoot

$uri = Get-AzureWinRMUri -ServiceName $svcname -Name $vmname

$vm = Get-AzureVM -ServiceName $svcname -Name $vmname
$certthumbrint = $vm.VM.DefaultWinRmCertificateThumbprint
$cert = Get-AzureCertificate -ServiceName $svcname `
    -Thumbprint $certthumbrint -ThumbprintAlgorithm sha1
$cert.Data | Out-File -FilePath ".\my_cert.cer"
certutil.exe -f -addstore Root .\my_cert.cer

$securepwd = ConvertTo-SecureString -String $pwd -AsPlainText -Force
$cred = New-Object `
    -TypeName System.Management.Automation.PSCredential `
    -ArgumentList "$vmName\$user", $securepwd

Invoke-Command -ConnectionUri $uri -Credential $cred -ScriptBlock {
    Import-Module -Name ServerManager
    Install-WindowsFeature -Name Web-Server -IncludeManagementTools
}

Pour terminer je ne peux que vous encourager à lire l’article cité plus haut :

https://michaelwasham.com/2013/04/16/windows-azure-powershell-updates-for-iaas-ga/

Il contient un exemple plus complet, même si je garde ma méthode certutil pour importer le certificat…

Plus d’informations également sur PowerShell Remoting ici :

https://blogs.technet.com/b/pascals/archive/2012/07/24/powershell-remoting.aspx